diff --git a/layouts/v7/lib/anchorme_js/LICENSE b/layouts/v7/lib/anchorme_js/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6ce4915b765f4d1f550f4a95a031e889752d5457 --- /dev/null +++ b/layouts/v7/lib/anchorme_js/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ali Saleem + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/layouts/v7/lib/anchorme_js/anchorme.js b/layouts/v7/lib/anchorme_js/anchorme.js new file mode 100644 index 0000000000000000000000000000000000000000..43e9a68e734bc1b3b5106da8938f71821655e875 --- /dev/null +++ b/layouts/v7/lib/anchorme_js/anchorme.js @@ -0,0 +1,431 @@ +/** + * + * + * Anchorme.js + * 0.6.0 + * @auth: Ali Saleem <ali.a.saleem@outlook.com> + * @repo: https://github.com/ali-saleem/anchorme.js + * + * + * + **/ + + + +(function(window) { + + 'use strict'; + + // ES6 endsWith polyfill + if (!String.prototype.endsWith) { + String.prototype.endsWith = function(searchString, position) { + var subjectString = this.toString(); + if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }; + } + + // ES6 startsWith polyfill + if (!String.prototype.startsWith) { + String.prototype.startsWith = function(searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; + } + + var anchorme = {}; + + // helper function that counts the occurences of a specific substring in a string + anchorme.occurrences = function(string, subString, allowOverlapping) { + string += ""; + subString += ""; + if (subString.length <= 0) return string.length + 1; + var n = 0, + pos = 0; + var step = (allowOverlapping) ? (1) : (subString.length); + while (true) { + pos = string.indexOf(subString, pos); + if (pos >= 0) { + n++; + pos += step; + } + else break; + } + return (n); + }; + + // function to avoid breaking of the HTML + anchorme.dontbreakHTML = function(str) { + var atrs = ["src","href","cite","formaction","icon","manifest","poster","codebase","background","profile","usemap"]; + for (var i = atrs.length; i--;) { + var needles = [atrs[i]+'=" ', atrs[i]+"=' "]; + var replacements = [atrs[i]+'="', atrs[i]+"='"]; + str = str.split(needles[0]).join(replacements[0]); + str = str.split(needles[1]).join(replacements[1]); + } + return str; + }; + + + anchorme.removeCharifItEndsWithIt = function(url,char) { + if (url.endsWith(char)) { + url = url.substring(0, url.length - 1); + return anchorme.removeCharifItEndsWithIt(url,char); + } + else return url; + }; + + + anchorme.TLDs = ['.com','.org','.edu','.gov','.uk','.net','.ca','.de','.jp','.fr','.au','.us','.ru','.ch','.it','.nl','.se','.no','.es','.io','.aero','.mil','.biz','.cat','.coop','.info','.jobs','.mobi','.museum','.name','.pro','.travel','.ac','.ad','.ae','.af','.ag','.ai','.al','.am','.an','.ao','.ap','.aq','.ar','.as','.at','.aw','.az','.ax','.ba','.bb','.bd','.be','.bf','.bg','.bh','.bi','.bj','.bm','.bn','.bo','.br','.bs','.bt','.bv','.bw','.by','.bz','.cc','.cd','.cf','.cg','.ci','.ck','.cl','.cm','.cn','.co','.cr','.cs','.cu','.cv','.cx','.cy','.cz','.dj','.dk','.dm','.do','.dz','.ec','.ee','.eg','.eh','.er','.et','.eu','.fi','.fj','.fk','.fm','.fo','.ga','.gb','.gd','.ge','.gf','.gg','.gh','.gi','.gl','.gm','.gn','.gp','.gq','.gr','.gs','.gt','.gu','.gw','.gy','.hk','.hm','.hn','.hr','.ht','.hu','.id','.ie','.il','.im','.in','.io','.iq','.ir','.is','.je','.jm','.jo','.ke','.kg','.kh','.ki','.km','.kn','.kp','.kr','.kw','.ky','.kz','.la','.lb','.lc','.li','.lk','.lr','.ls','.lt','.lu','.lv','.ly','.ma','.mc','.md','.mg','.mh','.mk','.ml','.mm','.mn','.mo','.mp','.mq','.mr','.ms','.mt','.mu','.mv','.mw','.mx','.my','.mz','.na','.nc','.ne','.nf','.ng','.ni','.np','.nr','.nu','.nz','.om','.pa','.pe','.pf','.pg','.ph','.pk','.pl','.pm','.pn','.pr','.ps','.pt','.pw','.py','.qa','.re','.ro','.rw','.sa','.sb','.sc','.sd','.sg','.sh','.si','.sj','.sk','.sl','.sm','.sn','.so','.sr','.st','.sv','.sy','.sz','.tc','.td','.tf','.tg','.th','.tj','.tk','.tl','.tm','.tn','.to','.tp','.tr','.tt','.tv','.tw','.tz','.ua','.ug','.um','.uy','.uz','.va','.vc','.ve','.vg','.vi','.vn','.vu','.wf','.ws','.ye','.yt','.yu','.za','.zm','.zw','.guru','.berlin','.photography','.tips','.today','.email','.technology','.company','.clothing','.me','.asia','.abb','.academy','.active','.actor','.ads','.adult','.afl','.agency','.aig','.airforce','.alsace','.amsterdam','.android','.apartments','.app','.aquarelle','.archi','.army','.associates','.attorney','.auction','.audio','.auto','.autos','.axa','.azure','.band','.bank','.bar','.barcelona','.barclays','.bargains','.bauhaus','.bayern','.bbc','.bbva','.bcn','.beer','.bentley','.best','.bharti','.bible','.bid','.bike','.bing','.bingo','.bio','.black','.blackfriday','.bloomberg','.blue','.bmw','.bnl','.bnpparibas','.boats','.bond','.boo','.boutique','.bradesco','.bridgestone','.broker','.brother','.brussels','.budapest','.build','.builders','.business','.buzz','.bzh','.cab','.cafe','.cal','.camera','.camp','.canon','.capetown','.capital','.caravan','.cards','.care','.career','.careers','.cars','.cartier','.casa','.cash','.casino','.catering','.cba','.cbn','.center','.ceo','.cern','.cfa','.cfd','.channel','.chat','.cheap','.chloe','.christmas','.chrome','.church','.cisco','.citic','.city','.claims','.cleaning','.click','.clinic','.cloud','.club','.coach','.codes','.coffee','.college','.cologne','.community','.computer','.condos','.construction','.consulting','.contractors','.cooking','.cool','.corsica','.country','.coupons','.courses','.credit','.creditcard','.cricket','.crown','.crs','.cruises','.cuisinella','.cw','.cymru','.cyou','.dabur','.dad','.dance','.date','.dating','.datsun','.day','.dclk','.deals','.degree','.delivery','.delta','.democrat','.dental','.dentist','.desi','.design','.dev','.diamonds','.diet','.digital','.direct','.directory','.discount','.dnp','.docs','.dog','.doha','.domains','.doosan','.download','.drive','.durban','.dvag','.earth','.eat','.education','.emerck','.energy','.engineer','.engineering','.enterprises','.epson','.equipment','.erni','.esq','.estate','.eus','.events','.everbank','.exchange','.expert','.exposed','.express','.fail','.faith','.fan','.fans','.farm','.fashion','.feedback','.film','.finance','.financial','.firmdale','.fish','.fishing','.fit','.fitness','.flights','.florist','.flowers','.flsmidth','.fly','.foo','.football','.forex','.forsale','.forum','.foundation','.frl','.frogans','.fund','.furniture','.futbol','.fyi','.gal','.gallery','.game','.garden','.gbiz','.gdn','.gent','.genting','.ggee','.gift','.gifts','.gives','.glass','.gle','.global','.globo','.gmail','.gmo','.gmx','.gold','.goldpoint','.golf','.goo','.goog','.google','.gop','.graphics','.gratis','.green','.gripe','.guge','.guide','.guitars','.hamburg','.hangout','.haus','.healthcare','.help','.here','.hermes','.hiphop','.hitachi','.hiv','.hockey','.holdings','.holiday','.homedepot','.homes','.honda','.horse','.host','.hosting','.hoteles','.hotmail','.house','.how','.hsbc','.ibm','.icbc','.icu','.ifm','.iinet','.immo','.immobilien','.industries','.infiniti','.ing','.ink','.institute','.insure','.int','.international','.investments','.irish','.ist','.istanbul','.iwc','.java','.jcb','.jetzt','.jewelry','.jlc','.jll','.joburg','.jprs','.juegos','.kaufen','.kddi','.kim','.kitchen','.kiwi','.koeln','.komatsu','.krd','.kred','.kyoto','.lacaixa','.land','.lasalle','.lat','.latrobe','.law','.lawyer','.lds','.lease','.leclerc','.legal','.lgbt','.liaison','.lidl','.life','.lighting','.limited','.limo','.link','.live','.loan','.loans','.lol','.london','.lotte','.lotto','.love','.ltda','.lupin','.luxe','.luxury','.madrid','.maif','.maison','.management','.mango','.market','.marketing','.markets','.marriott','.mba','.media','.meet','.melbourne','.meme','.memorial','.men','.menu','.miami','.microsoft','.mini','.mma','.moda','.moe','.monash','.money','.montblanc','.mormon','.mortgage','.moscow','.motorcycles','.mov','.movie','.movistar','.mtn','.mtpc','.nadex','.nagoya','.navy','.nec','.netbank','.network','.neustar','.new','.news','.nexus','.ngo','.nhk','.nico','.ninja','.nissan','.nra','.nrw','.ntt','.nyc','.office','.okinawa','.omega','.one','.ong','.onl','.online','.ooo','.oracle','.orange','.organic','.osaka','.otsuka','.ovh','.page','.panerai','.paris','.partners','.parts','.party','.pharmacy','.philips','.photo','.photos','.physio','.piaget','.pics','.pictet','.pictures','.pink','.pizza','.place','.play','.plumbing','.plus','.pohl','.poker','.porn','.post','.praxi','.press','.prod','.productions','.prof','.properties','.property','.pub','.qpon','.quebec','.racing','.realtor','.realty','.recipes','.red','.redstone','.rehab','.reise','.reisen','.reit','.ren','.rent','.rentals','.repair','.report','.republican','.rest','.restaurant','.review','.reviews','.rich','.ricoh','.rio','.rip','.rocks','.rodeo','.rs','.rsvp','.ruhr','.run','.ryukyu','.saarland','.sakura','.sale','.samsung','.sandvik','.sap','.sarl','.saxo','.sca','.scb','.school','.schule','.schwarz','.science','.seat','.sener','.services','.sew','.sex','.sexy','.shiksha','.shoes','.show','.shriram','.singles','.site','.ski','.sky','.skype','.sncf','.soccer','.social','.software','.sohu','.solar','.solutions','.sony','.soy','.space','.spiegel','.spreadbetting','.starhub','.statoil','.studio','.study','.style','.su','.sucks','.supplies','.supply','.support','.surf','.surgery','.suzuki','.swatch','.swiss','.sx','.sydney','.systems','.taipei','.tatar','.tattoo','.tax','.taxi','.team','.tech','.tel','.telefonica','.temasek','.tennis','.thd','.theater','.tickets','.tienda','.tires','.tirol','.tokyo','.tools','.top','.toray','.toshiba','.tours','.town','.toys','.trade','.trading','.training','.trust','.tui','.ubs','.university','.uno','.uol','.vacations','.vegas','.ventures','.versicherung','.vet','.viajes','.video','.villas','.vision','.vista','.vistaprint','.vlaanderen','.vodka','.vote','.voting','.voto','.voyage','.wales','.walter','.wang','.watch','.webcam','.website','.wed','.wedding','.weir','.wien','.wiki','.win','.windows','.wme','.work','.works','.world','.wtc','.wtf','.xbox','.xerox','.xin','.xxx','.xyz','.yandex','.youtube','.zip','.zone','.zuerich']; + anchorme.checks = {}; + anchorme.checks.ip = function (str) { + if (anchorme.occurrences(str, ".") > 2) { + var IPArray = str.split("."); + var oc1 = IPArray[0]; + var oc2 = IPArray[1]; + var oc3 = IPArray[2]; + if (IPArray[3].indexOf(":") > 0) { + var i_colon = IPArray[3].indexOf(":"); + var oc4 = IPArray[3].substring(0, i_colon); + var proOC = IPArray[3].substring(i_colon); + } + else if (IPArray[3].indexOf("/") > 0) { + var i_FoSlash = IPArray[3].indexOf("/"); + var oc4 = IPArray[3].substring(0, i_FoSlash); + var proOC = IPArray[3].substring(i_FoSlash); + } + else { + var oc4 = IPArray[3]; + var proOC = false; + } + if (proOC === false || proOC.charAt(0) === "/" || proOC.charAt(0) === ":") { + if ( + !isNaN(oc1) && + !isNaN(oc2) && + !isNaN(oc3) && + !isNaN(oc4) && + oc1 - 1 <= 254 && + oc2 - 1 <= 254 && + oc3 - 1 <= 254 && + oc4 - 1 <= 254 && + oc1.length > 0 && + oc2.length > 0 && + oc3.length > 0 && + oc4.length > 0 + ) {return true;} + } return false; + } return false; + }; + anchorme.checks.email = function (str,howManyTLDsToCheck) { + str = str.toLowerCase(); + if (anchorme.occurrences(str, "@") == 1) { + str = anchorme.removeCharifItEndsWithIt(str,"."); + str = anchorme.removeCharifItEndsWithIt(str,","); + str = anchorme.removeCharifItEndsWithIt(str,";"); + var i_at = str.indexOf("@"); + var Ename = str.substring(0, i_at); + var Eserver = str.substring(i_at + 1, str.length); + + var EmailAllowed = true; + for (var x = 0; x < Ename.length; x++) { + var char = Ename[x]; + if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~.".indexOf(char) === -1) { + x = Ename.length; + EmailAllowed = false; + } + } + + for (var x = 0; x < Eserver.length; x++) { + var cr = Eserver[x]; + if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(cr) === -1) { + x = Eserver.length; + EmailAllowed = false; + } + } + + if (EmailAllowed) { + var validTLD = false; + for (var x = 0; x < howManyTLDsToCheck; x++) { + var d = anchorme.TLDs[x]; + if (str.endsWith(d)) { + x = anchorme.TLDs.length; + validTLD = true; + } + } + if (validTLD === true) return true; + else return false; + } else return false; + } return false; + }; + anchorme.checks.url = function (str,howManyTLDsToCheck) { + str = str.toLowerCase(); + if(str.indexOf(".") > 0 && (str.indexOf("/") > 3 || str.indexOf("/") < 0)) { + str = anchorme.removeCharifItEndsWithIt(str,"."); + str = anchorme.removeCharifItEndsWithIt(str,","); + str = anchorme.removeCharifItEndsWithIt(str,";"); + if(anchorme.occurrences(str,".") == 1 && str.indexOf(".") === str.length - 1) return false; + var domainAllowed = true; + if (str.indexOf("/") > 3) { + var i_FoSlash = str.indexOf("/"); + var pre_slash = str.substring(0, i_FoSlash); + if(pre_slash.indexOf("..") > -1) return false; + for (var x = 0; x < pre_slash.length; x++) { + var cr = pre_slash[x]; + if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(cr) === -1) { + x = pre_slash.length; + domainAllowed = false; + } + } + } else { + if(str.indexOf("..") > -1) return false; + for (var x = 0; x < str.length; x++) { + var cr = str[x]; + if ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(cr) === -1) { + x = str.length; + domainAllowed = false; + } + } + } + if (domainAllowed) { + if (str.endsWith(".com")) return true; + for (var x = 0; x < howManyTLDsToCheck; x++) { + var tld = anchorme.TLDs[x]; + if (str.endsWith(tld) || str.indexOf(tld+"/") > -1 || str.indexOf(tld+":") > -1) { + x = anchorme.TLDs.length; + return true; + } + } + return false; + } else return false; + } else return false; + }; + anchorme.order = function(input, options) { + // split the input (the text we will work on) into an array + // and do checks on each item of this array + var splitedArray = input.split(" "); + for (var i = 0; i < splitedArray.length; i++) { + var fragment = splitedArray[i]; + var isurl = false; + var protocol = false; + + /** + * + * First check is to check the number of dots + * if there are more than 0 (i.e one or more) + * then this might by a URL + * else, this is surely not a URL + * + **/ + if(fragment.indexOf(".") > -1) { + /** + * second check, is to see if the fragment has any charecters + * that are not allowed neither in a URL, nor in an + * email, nor in an IP. + **/ + var allAllowed = true; + var URLallowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%"; + + for (var x = 0; x < fragment.length; x++) { + var cr = fragment[x]; + if (URLallowed.indexOf(cr) === -1) { + x = fragment.length; + allAllowed = false; + } + } + if (allAllowed) { + + /** + * third check is to see if the fragment begins with + * a protocol, it it does, then it's a URL no other + * tests will be done. + **/ + + if( options.urls && (fragment.startsWith("http://") || fragment.startsWith("HTTP://"))) isurl = true; + else if(options.urls && (fragment.startsWith("https://") || fragment.startsWith("HTTPS://"))) isurl = true; + else if(options.urls && (fragment.startsWith("ftp://") || fragment.startsWith("FTP://"))) isurl = true; + else if(options.urls && (fragment.startsWith("file:///") || fragment.startsWith("FILE:///"))) isurl = true; + else if(options.emails && (fragment.startsWith("mailto:") || fragment.startsWith("MAILTO:"))) isurl = true; + + /** + * + * Other checks are written in seperate functions + * they are: + * * a check for IP + * * a checK for email + * * a check for URL + **/ + + else if (anchorme.checks.ip(fragment) && options.ips && fragment.indexOf(".") > 0) { + isurl = true; + protocol = options.defaultProtocol; + } + + else if (anchorme.checks.email(fragment,options.TLDs) && options.emails && fragment.indexOf(".") > -1 && fragment.indexOf("@") > -1) { + isurl = true; + protocol = "mailto:"; + } + + else if (anchorme.checks.url(fragment,options.TLDs) && options.urls) { + isurl = true; + protocol = options.defaultProtocol; + } + + /** + * + * All checks are done now + * At this point, we either have a true value or false value + * of isurl + * if it's true, we need to modify the splittedArray fragment + * if it's false, no modifications will be done + * + **/ + + if (isurl) { + + var url = protocol ? protocol + fragment : fragment; + + url = anchorme.removeCharifItEndsWithIt(url,"."); + url = anchorme.removeCharifItEndsWithIt(url,","); + url = anchorme.removeCharifItEndsWithIt(url,";"); + + if (options.attributes) { + splitedArray[i] = "<a href='" + url + "'"; + for (var name in options.attributes) { + splitedArray[i] = splitedArray[i] + " " + name + "='" + options.attributes[name] + "' "; + } + splitedArray[i] = splitedArray[i] + ">" + fragment + "</a>"; + } + else { + splitedArray[i] = "<a href='" + url + "'>" + fragment + "</a>"; + } + } + } + } + } + return splitedArray.join(" "); + }; + + // function to be called by user + anchorme.js = function(str, options) { + + // Starts by assigning the default options + // if the whole options object is ommited + if(typeof options !== "object") { + options = { + "attributes":false, + "html":true, + ips:true, + emails:true, + urls:true, + TLDs:20, + defaultProtocol:"http://" + }; + } + + // if some options were ommited and some options were given: + else { + if(typeof options.attributes != "object") options.attributes = false; + if(typeof options.html != "boolean") options.html = true; + if(typeof options.ips != "boolean") options.ips = true; + if(typeof options.emails != "boolean") options.emails = true; + if(typeof options.urls != "boolean") options.urls = true; + if(typeof options.TLDs != "number") options.TLDs = 20; + if(typeof options.defaultProtocol != "string") options.defaultProtocol = "http://"; + } + + if(options.html){ + if( + str.indexOf("</a>") > -1 || + str.indexOf("<img ") > -1 || + str.indexOf("<blockquote ") > -1 || + str.indexOf("<del ") > -1 || + str.indexOf("<iframe ") > -1 || + str.indexOf("<script ") > -1 || + str.indexOf("<audio ") > -1 || + str.indexOf("<button ") > -1 || + str.indexOf("<command ") > -1 || + str.indexOf("<embed ") > -1 || + str.indexOf("<html ") > -1 || + str.indexOf("<video ") > -1 || + str.indexOf("<applet ") > -1 || + str.indexOf("<area ") > -1 || + str.indexOf("<base ") > -1 || + str.indexOf("<body ") > -1 || + str.indexOf("<frame ") > -1 || + str.indexOf("<head ") > -1 || + str.indexOf("<usemap ") > -1 || + str.indexOf("<link ") > -1 || + str.indexOf("<input ") > -1 || + str.indexOf("<source ") > -1 || + str.indexOf("<q ") > -1 + ) str = anchorme.dontbreakHTML(str); + } + + /** + * + * Since anchorme.js runs by seperating words by spaces, + * we have to take in consideration if there were other + * charecters seperating the URL from other words, + * such as: line breaks, braces and angle brackets + * + **/ + + str = str.split("\n").join(" \n "); + str = str.split("(").join(" ( "); + str = str.split(")").join(" ) "); + str = str.split("<").join(" < "); + str = str.split(">").join(" > "); + + /** + * + * The "order" method will actually do all the heavy lifting + * and return the end result + * + **/ + + var result = anchorme.order(str, options); + + /** + * + * After getting the end result, we need to remove + * the extra spaces we added from the result + * + **/ + + result = result.split(" \n ").join("\n"); + result = result.split(" ( ").join("("); + result = result.split(" ) ").join(")"); + result = result.split(" < ").join("<"); + result = result.split(" > ").join(">"); + + // return the result + return result; + }; + + + // check js environment + if (typeof exports !== "undefined") { + // nodejs env + if (typeof module !== "undefined" && module.exports) { + exports = module.exports = anchorme; + } + exports.anchorme = anchorme; + } + else { + // requirejs env (optional) + if (typeof define === "function" && define.amd) { + define("anchorme", [], function() { + return anchorme; + }); + } + else { + // browser env + window.anchorme = anchorme; + } + } + +})(typeof window === 'object' ? window : this /* it runs in node */ ); \ No newline at end of file diff --git a/layouts/v7/lib/anchorme_js/anchorme.min.js b/layouts/v7/lib/anchorme_js/anchorme.min.js new file mode 100644 index 0000000000000000000000000000000000000000..42de76135fd94eb1ad9ae1bb2a9bb938d1d11610 --- /dev/null +++ b/layouts/v7/lib/anchorme_js/anchorme.min.js @@ -0,0 +1 @@ +!function(e){"use strict";String.prototype.endsWith||(String.prototype.endsWith=function(e,t){var i=this.toString();("number"!=typeof t||!isFinite(t)||Math.floor(t)!==t||t>i.length)&&(t=i.length),t-=e.length;var n=i.indexOf(e,t);return-1!==n&&n===t}),String.prototype.startsWith||(String.prototype.startsWith=function(e,t){return t=t||0,this.substr(t,e.length)===e});var t={};t.occurrences=function(e,t,i){if(e+="",t+="",t.length<=0)return e.length+1;for(var n=0,r=0,a=i?1:t.length;;){if(r=e.indexOf(t,r),!(r>=0))break;n++,r+=a}return n},t.dontbreakHTML=function(e){for(var t=["src","href","cite","formaction","icon","manifest","poster","codebase","background","profile","usemap"],i=t.length;i--;){var n=[t[i]+'=" ',t[i]+"=' "],r=[t[i]+'="',t[i]+"='"];e=e.split(n[0]).join(r[0]),e=e.split(n[1]).join(r[1])}return e},t.removeCharifItEndsWithIt=function(e,i){return e.endsWith(i)?(e=e.substring(0,e.length-1),t.removeCharifItEndsWithIt(e,i)):e},t.TLDs=[".com",".org",".edu",".gov",".uk",".net",".ca",".de",".jp",".fr",".au",".us",".ru",".ch",".it",".nl",".se",".no",".es",".io",".aero",".mil",".biz",".cat",".coop",".info",".jobs",".mobi",".museum",".name",".pro",".travel",".ac",".ad",".ae",".af",".ag",".ai",".al",".am",".an",".ao",".ap",".aq",".ar",".as",".at",".aw",".az",".ax",".ba",".bb",".bd",".be",".bf",".bg",".bh",".bi",".bj",".bm",".bn",".bo",".br",".bs",".bt",".bv",".bw",".by",".bz",".cc",".cd",".cf",".cg",".ci",".ck",".cl",".cm",".cn",".co",".cr",".cs",".cu",".cv",".cx",".cy",".cz",".dj",".dk",".dm",".do",".dz",".ec",".ee",".eg",".eh",".er",".et",".eu",".fi",".fj",".fk",".fm",".fo",".ga",".gb",".gd",".ge",".gf",".gg",".gh",".gi",".gl",".gm",".gn",".gp",".gq",".gr",".gs",".gt",".gu",".gw",".gy",".hk",".hm",".hn",".hr",".ht",".hu",".id",".ie",".il",".im",".in",".io",".iq",".ir",".is",".je",".jm",".jo",".ke",".kg",".kh",".ki",".km",".kn",".kp",".kr",".kw",".ky",".kz",".la",".lb",".lc",".li",".lk",".lr",".ls",".lt",".lu",".lv",".ly",".ma",".mc",".md",".mg",".mh",".mk",".ml",".mm",".mn",".mo",".mp",".mq",".mr",".ms",".mt",".mu",".mv",".mw",".mx",".my",".mz",".na",".nc",".ne",".nf",".ng",".ni",".np",".nr",".nu",".nz",".om",".pa",".pe",".pf",".pg",".ph",".pk",".pl",".pm",".pn",".pr",".ps",".pt",".pw",".py",".qa",".re",".ro",".rw",".sa",".sb",".sc",".sd",".sg",".sh",".si",".sj",".sk",".sl",".sm",".sn",".so",".sr",".st",".sv",".sy",".sz",".tc",".td",".tf",".tg",".th",".tj",".tk",".tl",".tm",".tn",".to",".tp",".tr",".tt",".tv",".tw",".tz",".ua",".ug",".um",".uy",".uz",".va",".vc",".ve",".vg",".vi",".vn",".vu",".wf",".ws",".ye",".yt",".yu",".za",".zm",".zw",".guru",".berlin",".photography",".tips",".today",".email",".technology",".company",".clothing",".me",".asia",".abb",".academy",".active",".actor",".ads",".adult",".afl",".agency",".aig",".airforce",".alsace",".amsterdam",".android",".apartments",".app",".aquarelle",".archi",".army",".associates",".attorney",".auction",".audio",".auto",".autos",".axa",".azure",".band",".bank",".bar",".barcelona",".barclays",".bargains",".bauhaus",".bayern",".bbc",".bbva",".bcn",".beer",".bentley",".best",".bharti",".bible",".bid",".bike",".bing",".bingo",".bio",".black",".blackfriday",".bloomberg",".blue",".bmw",".bnl",".bnpparibas",".boats",".bond",".boo",".boutique",".bradesco",".bridgestone",".broker",".brother",".brussels",".budapest",".build",".builders",".business",".buzz",".bzh",".cab",".cafe",".cal",".camera",".camp",".canon",".capetown",".capital",".caravan",".cards",".care",".career",".careers",".cars",".cartier",".casa",".cash",".casino",".catering",".cba",".cbn",".center",".ceo",".cern",".cfa",".cfd",".channel",".chat",".cheap",".chloe",".christmas",".chrome",".church",".cisco",".citic",".city",".claims",".cleaning",".click",".clinic",".cloud",".club",".coach",".codes",".coffee",".college",".cologne",".community",".computer",".condos",".construction",".consulting",".contractors",".cooking",".cool",".corsica",".country",".coupons",".courses",".credit",".creditcard",".cricket",".crown",".crs",".cruises",".cuisinella",".cw",".cymru",".cyou",".dabur",".dad",".dance",".date",".dating",".datsun",".day",".dclk",".deals",".degree",".delivery",".delta",".democrat",".dental",".dentist",".desi",".design",".dev",".diamonds",".diet",".digital",".direct",".directory",".discount",".dnp",".docs",".dog",".doha",".domains",".doosan",".download",".drive",".durban",".dvag",".earth",".eat",".education",".emerck",".energy",".engineer",".engineering",".enterprises",".epson",".equipment",".erni",".esq",".estate",".eus",".events",".everbank",".exchange",".expert",".exposed",".express",".fail",".faith",".fan",".fans",".farm",".fashion",".feedback",".film",".finance",".financial",".firmdale",".fish",".fishing",".fit",".fitness",".flights",".florist",".flowers",".flsmidth",".fly",".foo",".football",".forex",".forsale",".forum",".foundation",".frl",".frogans",".fund",".furniture",".futbol",".fyi",".gal",".gallery",".game",".garden",".gbiz",".gdn",".gent",".genting",".ggee",".gift",".gifts",".gives",".glass",".gle",".global",".globo",".gmail",".gmo",".gmx",".gold",".goldpoint",".golf",".goo",".goog",".google",".gop",".graphics",".gratis",".green",".gripe",".guge",".guide",".guitars",".hamburg",".hangout",".haus",".healthcare",".help",".here",".hermes",".hiphop",".hitachi",".hiv",".hockey",".holdings",".holiday",".homedepot",".homes",".honda",".horse",".host",".hosting",".hoteles",".hotmail",".house",".how",".hsbc",".ibm",".icbc",".icu",".ifm",".iinet",".immo",".immobilien",".industries",".infiniti",".ing",".ink",".institute",".insure",".int",".international",".investments",".irish",".ist",".istanbul",".iwc",".java",".jcb",".jetzt",".jewelry",".jlc",".jll",".joburg",".jprs",".juegos",".kaufen",".kddi",".kim",".kitchen",".kiwi",".koeln",".komatsu",".krd",".kred",".kyoto",".lacaixa",".land",".lasalle",".lat",".latrobe",".law",".lawyer",".lds",".lease",".leclerc",".legal",".lgbt",".liaison",".lidl",".life",".lighting",".limited",".limo",".link",".live",".loan",".loans",".lol",".london",".lotte",".lotto",".love",".ltda",".lupin",".luxe",".luxury",".madrid",".maif",".maison",".management",".mango",".market",".marketing",".markets",".marriott",".mba",".media",".meet",".melbourne",".meme",".memorial",".men",".menu",".miami",".microsoft",".mini",".mma",".moda",".moe",".monash",".money",".montblanc",".mormon",".mortgage",".moscow",".motorcycles",".mov",".movie",".movistar",".mtn",".mtpc",".nadex",".nagoya",".navy",".nec",".netbank",".network",".neustar",".new",".news",".nexus",".ngo",".nhk",".nico",".ninja",".nissan",".nra",".nrw",".ntt",".nyc",".office",".okinawa",".omega",".one",".ong",".onl",".online",".ooo",".oracle",".orange",".organic",".osaka",".otsuka",".ovh",".page",".panerai",".paris",".partners",".parts",".party",".pharmacy",".philips",".photo",".photos",".physio",".piaget",".pics",".pictet",".pictures",".pink",".pizza",".place",".play",".plumbing",".plus",".pohl",".poker",".porn",".post",".praxi",".press",".prod",".productions",".prof",".properties",".property",".pub",".qpon",".quebec",".racing",".realtor",".realty",".recipes",".red",".redstone",".rehab",".reise",".reisen",".reit",".ren",".rent",".rentals",".repair",".report",".republican",".rest",".restaurant",".review",".reviews",".rich",".ricoh",".rio",".rip",".rocks",".rodeo",".rs",".rsvp",".ruhr",".run",".ryukyu",".saarland",".sakura",".sale",".samsung",".sandvik",".sap",".sarl",".saxo",".sca",".scb",".school",".schule",".schwarz",".science",".seat",".sener",".services",".sew",".sex",".sexy",".shiksha",".shoes",".show",".shriram",".singles",".site",".ski",".sky",".skype",".sncf",".soccer",".social",".software",".sohu",".solar",".solutions",".sony",".soy",".space",".spiegel",".spreadbetting",".starhub",".statoil",".studio",".study",".style",".su",".sucks",".supplies",".supply",".support",".surf",".surgery",".suzuki",".swatch",".swiss",".sx",".sydney",".systems",".taipei",".tatar",".tattoo",".tax",".taxi",".team",".tech",".tel",".telefonica",".temasek",".tennis",".thd",".theater",".tickets",".tienda",".tires",".tirol",".tokyo",".tools",".top",".toray",".toshiba",".tours",".town",".toys",".trade",".trading",".training",".trust",".tui",".ubs",".university",".uno",".uol",".vacations",".vegas",".ventures",".versicherung",".vet",".viajes",".video",".villas",".vision",".vista",".vistaprint",".vlaanderen",".vodka",".vote",".voting",".voto",".voyage",".wales",".walter",".wang",".watch",".webcam",".website",".wed",".wedding",".weir",".wien",".wiki",".win",".windows",".wme",".work",".works",".world",".wtc",".wtf",".xbox",".xerox",".xin",".xxx",".xyz",".yandex",".youtube",".zip",".zone",".zuerich"],t.checks={},t.checks.ip=function(e){if(t.occurrences(e,".")>2){var i=e.split("."),n=i[0],r=i[1],a=i[2];if(i[3].indexOf(":")>0)var s=i[3].indexOf(":"),o=i[3].substring(0,s),l=i[3].substring(s);else if(i[3].indexOf("/")>0)var c=i[3].indexOf("/"),o=i[3].substring(0,c),l=i[3].substring(c);else var o=i[3],l=!1;return(l===!1||"/"===l.charAt(0)||":"===l.charAt(0))&&!isNaN(n)&&!isNaN(r)&&!isNaN(a)&&!isNaN(o)&&254>=n-1&&254>=r-1&&254>=a-1&&254>=o-1&&n.length>0&&r.length>0&&a.length>0&&o.length>0?!0:!1}return!1},t.checks.email=function(e,i){if(e=e.toLowerCase(),1==t.occurrences(e,"@")){e=t.removeCharifItEndsWithIt(e,"."),e=t.removeCharifItEndsWithIt(e,","),e=t.removeCharifItEndsWithIt(e,";");for(var n=e.indexOf("@"),r=e.substring(0,n),a=e.substring(n+1,e.length),s=!0,o=0;o<r.length;o++){var l=r[o];-1==="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-/=?^_`{|}~.".indexOf(l)&&(o=r.length,s=!1)}for(var o=0;o<a.length;o++){var c=a[o];-1==="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(c)&&(o=a.length,s=!1)}if(s){for(var d=!1,o=0;i>o;o++){var u=t.TLDs[o];e.endsWith(u)&&(o=t.TLDs.length,d=!0)}return d===!0?!0:!1}return!1}return!1},t.checks.url=function(e,i){if(e=e.toLowerCase(),e.indexOf(".")>0&&(e.indexOf("/")>3||e.indexOf("/")<0)){if(e=t.removeCharifItEndsWithIt(e,"."),e=t.removeCharifItEndsWithIt(e,","),e=t.removeCharifItEndsWithIt(e,";"),1==t.occurrences(e,".")&&e.indexOf(".")===e.length-1)return!1;var n=!0;if(e.indexOf("/")>3){var r=e.indexOf("/"),a=e.substring(0,r);if(a.indexOf("..")>-1)return!1;for(var s=0;s<a.length;s++){var o=a[s];-1==="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(o)&&(s=a.length,n=!1)}}else{if(e.indexOf("..")>-1)return!1;for(var s=0;s<e.length;s++){var o=e[s];-1==="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.:".indexOf(o)&&(s=e.length,n=!1)}}if(n){if(e.endsWith(".com"))return!0;for(var s=0;i>s;s++){var l=t.TLDs[s];if(e.endsWith(l)||e.indexOf(l+"/")>-1||e.indexOf(l+":")>-1)return s=t.TLDs.length,!0}return!1}return!1}return!1},t.order=function(e,i){for(var n=e.split(" "),r=0;r<n.length;r++){var a=n[r],s=!1,o=!1;if(a.indexOf(".")>-1){for(var l=!0,c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=%",d=0;d<a.length;d++){var u=a[d];-1===c.indexOf(u)&&(d=a.length,l=!1)}if(l&&(i.urls&&(a.startsWith("http://")||a.startsWith("HTTP://"))?s=!0:i.urls&&(a.startsWith("https://")||a.startsWith("HTTPS://"))?s=!0:i.urls&&(a.startsWith("ftp://")||a.startsWith("FTP://"))?s=!0:i.urls&&(a.startsWith("file:///")||a.startsWith("FILE:///"))?s=!0:i.emails&&(a.startsWith("mailto:")||a.startsWith("MAILTO:"))?s=!0:t.checks.ip(a)&&i.ips&&a.indexOf(".")>0?(s=!0,o=i.defaultProtocol):t.checks.email(a,i.TLDs)&&i.emails&&a.indexOf(".")>-1&&a.indexOf("@")>-1?(s=!0,o="mailto:"):t.checks.url(a,i.TLDs)&&i.urls&&(s=!0,o=i.defaultProtocol),s)){var f=o?o+a:a;if(f=t.removeCharifItEndsWithIt(f,"."),f=t.removeCharifItEndsWithIt(f,","),f=t.removeCharifItEndsWithIt(f,";"),i.attributes){n[r]="<a href='"+f+"'";for(var h in i.attributes)n[r]=n[r]+" "+h+"='"+i.attributes[h]+"' ";n[r]=n[r]+">"+a+"</a>"}else n[r]="<a href='"+f+"'>"+a+"</a>"}}}return n.join(" ")},t.js=function(e,i){"object"!=typeof i?i={attributes:!1,html:!0,ips:!0,emails:!0,urls:!0,TLDs:20,defaultProtocol:"http://"}:("object"!=typeof i.attributes&&(i.attributes=!1),"boolean"!=typeof i.html&&(i.html=!0),"boolean"!=typeof i.ips&&(i.ips=!0),"boolean"!=typeof i.emails&&(i.emails=!0),"boolean"!=typeof i.urls&&(i.urls=!0),"number"!=typeof i.TLDs&&(i.TLDs=20),"string"!=typeof i.defaultProtocol&&(i.defaultProtocol="http://")),i.html&&(e.indexOf("</a>")>-1||e.indexOf("<img ")>-1||e.indexOf("<blockquote ")>-1||e.indexOf("<del ")>-1||e.indexOf("<iframe ")>-1||e.indexOf("<script ")>-1||e.indexOf("<audio ")>-1||e.indexOf("<button ")>-1||e.indexOf("<command ")>-1||e.indexOf("<embed ")>-1||e.indexOf("<html ")>-1||e.indexOf("<video ")>-1||e.indexOf("<applet ")>-1||e.indexOf("<area ")>-1||e.indexOf("<base ")>-1||e.indexOf("<body ")>-1||e.indexOf("<frame ")>-1||e.indexOf("<head ")>-1||e.indexOf("<usemap ")>-1||e.indexOf("<link ")>-1||e.indexOf("<input ")>-1||e.indexOf("<source ")>-1||e.indexOf("<q ")>-1)&&(e=t.dontbreakHTML(e)),e=e.split("\n").join(" \n "),e=e.split("(").join(" ( "),e=e.split(")").join(" ) "),e=e.split("<").join(" < "),e=e.split(">").join(" > ");var n=t.order(e,i);return n=n.split(" \n ").join("\n"),n=n.split(" ( ").join("("),n=n.split(" ) ").join(")"),n=n.split(" < ").join("<"),n=n.split(" > ").join(">")},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=t),exports.anchorme=t):"function"==typeof define&&define.amd?define("anchorme",[],function(){return t}):e.anchorme=t}("object"==typeof window?window:this); \ No newline at end of file diff --git a/layouts/v7/modules/Vtiger/Comment.tpl b/layouts/v7/modules/Vtiger/Comment.tpl index 36b2595bceadc7ad51f3df25f5fcacf606fd36b3..5980b51dab69187181379bbd213795532c49085b 100644 --- a/layouts/v7/modules/Vtiger/Comment.tpl +++ b/layouts/v7/modules/Vtiger/Comment.tpl @@ -45,7 +45,10 @@ </a> </span> {/if} - <div class=""> + <span class="commentTime text-muted cursorDefault"> + <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getCommentedTime())}">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getCommentedTime())}</small> + </span> + <div class="commentInfoContentBlock"> <span class="commentInfoContent"> {nl2br($COMMENT->get('commentcontent'))} </span> @@ -76,44 +79,46 @@ {if $CHILD_COMMENTS_MODEL neq null and ($CHILDS_ROOT_PARENT_ID neq $PARENT_COMMENT_ID)} {if $COMMENTS_MODULE_MODEL->isPermitted('EditView')} {/if} <span class="viewThreadBlock" data-child-comments-count="{$CHILD_COMMENTS_COUNT}"> - <a href="javascript:void(0)" class="cursorPointer viewThread"> + <a href="javascript:void(0)" class="cursorPointer viewThread" style="color: blue;"> <span class="childCommentsCount">{$CHILD_COMMENTS_COUNT}</span> {if $CHILD_COMMENTS_COUNT eq 1}{vtranslate('LBL_REPLY',$MODULE_NAME)}{else}{vtranslate('LBL_REPLIES',$MODULE_NAME)}{/if} </a> </span> <span class="hideThreadBlock" data-child-comments-count="{$CHILD_COMMENTS_COUNT}" style="display:none;"> - <a href="javascript:void(0)" class="cursorPointer hideThread"> + <a href="javascript:void(0)" class="cursorPointer hideThread" style="color: blue;"> <span class="childCommentsCount">{$CHILD_COMMENTS_COUNT}</span> {if $CHILD_COMMENTS_COUNT eq 1}{vtranslate('LBL_REPLY',$MODULE_NAME)}{else}{vtranslate('LBL_REPLIES',$MODULE_NAME)}{/if} </a> </span> {elseif $CHILD_COMMENTS_MODEL neq null and ($CHILDS_ROOT_PARENT_ID eq $PARENT_COMMENT_ID)} {if $COMMENTS_MODULE_MODEL->isPermitted('EditView')} {/if} <span class="viewThreadBlock" data-child-comments-count="{$CHILD_COMMENTS_COUNT}" style="display:none;"> - <a href="javascript:void(0)" class="cursorPointer viewThread"> + <a href="javascript:void(0)" class="cursorPointer viewThread" style="color: blue;"> <span class="childCommentsCount">{$CHILD_COMMENTS_COUNT}</span> {if $CHILD_COMMENTS_COUNT eq 1}{vtranslate('LBL_REPLY',$MODULE_NAME)}{else}{vtranslate('LBL_REPLIES',$MODULE_NAME)}{/if} </a> </span> <span class="hideThreadBlock" data-child-comments-count="{$CHILD_COMMENTS_COUNT}"> - <a href="javascript:void(0)" class="cursorPointer hideThread"> + <a href="javascript:void(0)" class="cursorPointer hideThread" style="color: blue;"> <span class="childCommentsCount">{$CHILD_COMMENTS_COUNT}</span> {if $CHILD_COMMENTS_COUNT eq 1}{vtranslate('LBL_REPLY',$MODULE_NAME)}{else}{vtranslate('LBL_REPLIES',$MODULE_NAME)}{/if} </a> </span> {/if} </span> - - <span class="commentTime text-muted cursorDefault" style="padding:20px;"> - <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getCommentedTime())}">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getCommentedTime())}</small> - </span> </div> - <br> {assign var="REASON_TO_EDIT" value=$COMMENT->get('reasontoedit')} - <div class="editedStatus" name="editStatus"> - <div class="{if empty($REASON_TO_EDIT)}hide{/if} editReason"> - <p class="text-muted"><small>[ {vtranslate('LBL_EDIT_REASON',$MODULE_NAME)} ] : <span name="editReason" class="textOverflowEllipsis">{nl2br($REASON_TO_EDIT)}</span></small></p> + {if $COMMENT->getCommentedTime() neq $COMMENT->getModifiedTime()} + <br> + <div class="commentEditStatus" name="editStatus"> + {assign var="REASON_TO_EDIT" value=$COMMENT->get('reasontoedit')} + {if $REASON_TO_EDIT} + <div class="text-muted"> + <small>{vtranslate('LBL_EDIT_REASON',$MODULE_NAME)} : <span name="editReason" class="textOverflowEllipsis">{nl2br($REASON_TO_EDIT)}</span></small> + </div> + {/if} + <div style="margin-top:5px;" class="text-muted"> + <small>{vtranslate('LBL_COMMENT',$MODULE_NAME)} {strtolower(vtranslate('LBL_MODIFIED',$MODULE_NAME))}</small> + <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getModifiedTime())}" class="commentModifiedTime">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getModifiedTime())}</small> + </div> </div> - {if $COMMENT->getCommentedTime() neq $COMMENT->getModifiedTime()} - <p class="text-muted cursorDefault"><small><em>{vtranslate('LBL_MODIFIED',$MODULE_NAME)}</em></small> <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getModifiedTime())}" class="commentModifiedTime">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getModifiedTime())}</small></p> - {/if} - </div> + {/if} <div style="margin-top:5px;"> {assign var="FILE_DETAILS" value=$COMMENT->getFileNameAndDownloadURL()} {foreach key=index item=FILE_DETAIL from=$FILE_DETAILS} @@ -136,12 +141,11 @@ </div> </div> </div> - <hr> </div> </div> </div> - </div> </div> + <hr> </div> {/strip} \ No newline at end of file diff --git a/layouts/v7/modules/Vtiger/JSResources.tpl b/layouts/v7/modules/Vtiger/JSResources.tpl index 792197c07d45ce6edbd62fa629fc799ab06e4bbe..0f2ad260a753620b7fd5004fbf5d01a0aa916e3a 100644 --- a/layouts/v7/modules/Vtiger/JSResources.tpl +++ b/layouts/v7/modules/Vtiger/JSResources.tpl @@ -36,6 +36,7 @@ <script type="text/javascript" src="layouts/v7/lib/jquery/jquery.timeago.js"></script> <script type="text/javascript" src="libraries/jquery/ckeditor/ckeditor.js"></script> <script type="text/javascript" src="libraries/jquery/ckeditor/adapters/jquery.js"></script> + <script type='text/javascript' src='layouts/v7/lib/anchorme_js/anchorme.min.js'></script> <script type="text/javascript" src="{vresource_url('layouts/v7/modules/Vtiger/resources/Class.js')}"></script> <script type='text/javascript' src="{vresource_url('layouts/v7/resources/helper.js')}"></script> <script type="text/javascript" src="{vresource_url('layouts/v7/resources/application.js')}"></script> diff --git a/layouts/v7/modules/Vtiger/RecentComments.tpl b/layouts/v7/modules/Vtiger/RecentComments.tpl index bb7aebc7dcdbd9826e269e6f2173050d84ef411c..a12f69e17f7c8aafba90529c2e9fea5d1e58f895 100644 --- a/layouts/v7/modules/Vtiger/RecentComments.tpl +++ b/layouts/v7/modules/Vtiger/RecentComments.tpl @@ -20,7 +20,7 @@ <div class="row"> <div class=" col-lg-12"> <div class="commentTextArea "> - <textarea name="commentcontent" class="commentcontent form-control mention_listener" placeholder="{vtranslate('LBL_POST_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> + <textarea name="commentcontent" class="commentcontent form-control col-lg-12" placeholder="{vtranslate('LBL_POST_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> </div> </div> </div> @@ -98,7 +98,7 @@ <span class="text-muted wordbreak display-inline-block"> {vtranslate('LBL_ON','Vtiger')} {vtranslate($SINGULR_MODULE,$COMMENT->get('module'))} - <a href="index.php?module={$COMMENT->get('module')}&view=Detail&record={$COMMENT->get('related_to')}"> + <a href="index.php?module={$COMMENT->get('module')}&view=Detail&record={$COMMENT->get('related_to')}" style="color: blue;"> {$ENTITY_NAME[$COMMENT->get('related_to')]} </a> </span> @@ -107,15 +107,16 @@ <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getCommentedTime())}">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getCommentedTime())}</small> </span> - <div class=""> + <div class="commentInfoContentBlock"> {assign var=COMMENT_CONTENT value={nl2br($COMMENT->get('commentcontent'))}} {if $COMMENT_CONTENT} {assign var=DISPLAYNAME value={decode_html($COMMENT_CONTENT)}} - <span class="commentInfoContent" style="display: block" data-fullComment="{$COMMENT_CONTENT|escape:"html"}" data-shortComment="{$DISPLAYNAME|mb_substr:0:200|escape:"html"}..." data-more='{vtranslate('LBL_SHOW_MORE',$MODULE)}' data-less='{vtranslate('LBL_SHOW',$MODULE)} {vtranslate('LBL_LESS',$MODULE)}'> - {if $DISPLAYNAME|count_characters:true gt 200} - {mb_substr(trim($DISPLAYNAME),0,200)}... - <br><a class="pull-right toggleComment showMore"><small>{vtranslate('LBL_SHOW_MORE',$MODULE)}</small></a> - {else} + {assign var=MAX_LENGTH value=200} + <span class="commentInfoContent" data-maxlength="{$MAX_LENGTH}" style="display: block" data-fullComment="{$COMMENT_CONTENT|escape:"html"}" data-shortComment="{$DISPLAYNAME|mb_substr:0:200|escape:"html"}..." data-more='{vtranslate('LBL_SHOW_MORE',$MODULE)}' data-less='{vtranslate('LBL_SHOW',$MODULE)} {vtranslate('LBL_LESS',$MODULE)}'> + {if $DISPLAYNAME|count_characters:true gt $MAX_LENGTH} + {mb_substr(trim($DISPLAYNAME),0,$MAX_LENGTH)}... + <a class="pull-right toggleComment showMore" style="color: blue;"><small>{vtranslate('LBL_SHOW_MORE',$MODULE)}</small></a> + {else} {$COMMENT_CONTENT} {/if} </span> @@ -128,10 +129,10 @@ <div class="commentAttachmentName"> <div class="filePreview clearfix"> <span class="fa fa-paperclip cursorPointer" ></span> - <a class="previewfile" onclick="Vtiger_Detail_Js.previewFile(event,{$COMMENT->get('id')},{$FILE_DETAIL['attachmentId']});" data-filename="{$FILE_NAME}" href="javascript:void(0)" name="viewfile"> + <a class="previewfile" onclick="Vtiger_Detail_Js.previewFile(event,{$COMMENT->get('id')},{$FILE_DETAIL['attachmentId']});" data-filename="{$FILE_NAME}" href="javascript:void(0)" name="viewfile" style="color: blue;"> <span title="{$FILE_DETAIL['rawFileName']}" style="line-height:1.5em;">{$FILE_NAME}</span>  </a> - <a name="downloadfile" href="{$FILE_DETAIL['url']}"> + <a name="downloadfile" href="{$FILE_DETAIL['url']}" style="color: blue;"> <i title="{vtranslate('LBL_DOWNLOAD_FILE',$MODULE_NAME)}" class="hide fa fa-download alignMiddle" ></i> </a> </div> @@ -142,7 +143,7 @@ <div class="commentActionsContainer" style="margin-top: 2px;"> <span> {if $PARENT_COMMENT_MODEL neq false or $CHILD_COMMENTS_MODEL neq null} - <a href="javascript:void(0);" class="cursorPointer detailViewThread">{vtranslate('LBL_VIEW_THREAD',$MODULE_NAME)}</a> + <a href="javascript:void(0);" class="cursorPointer detailViewThread" style="color: blue;">{vtranslate('LBL_VIEW_THREAD',$MODULE_NAME)}</a> {/if} </span> <span class="summarycommemntActionblock" > @@ -160,30 +161,24 @@ {/if} </span> </div> - <br> - <div class="row commentEditStatus marginBottom10px" name="editStatus"> - {assign var="REASON_TO_EDIT" value=$COMMENT->get('reasontoedit')} - <span class="col-lg-5 col-md-5 col-sm-5{if empty($REASON_TO_EDIT)} hide{/if}"> - <small> [{vtranslate('LBL_EDIT_REASON',$MODULE_NAME)}]</small> - </span> - {if $COMMENT->getCommentedTime() neq $COMMENT->getModifiedTime()} - <span class="{if empty($REASON_TO_EDIT)}row{else} col-lg-7 col-md-7 col-sm-7{/if}"> - <p class="text-muted pull-right"> - <small><em>{vtranslate('LBL_MODIFIED',$MODULE_NAME)}</em></small> + {if $COMMENT->getCommentedTime() neq $COMMENT->getModifiedTime()} + <br> + <div class="row commentEditStatus" name="editStatus"> + {assign var="REASON_TO_EDIT" value=$COMMENT->get('reasontoedit')} + {if $REASON_TO_EDIT} + <span class="text-muted col-lg-5 col-md-5 col-sm-5"> + <small>{vtranslate('LBL_EDIT_REASON',$MODULE_NAME)} : <span name="editReason" class="textOverflowEllipsis">{nl2br($REASON_TO_EDIT)}</span></small> + </span> + {/if} + <span {if $REASON_TO_EDIT}class="col-lg-7 col-md-7 col-sm-7"{/if}> + <p class="text-muted pull-right" {if !$REASON_TO_EDIT}style="margin-right: 15px;"{/if}> + <small>{vtranslate('LBL_COMMENT',$MODULE_NAME)} {strtolower(vtranslate('LBL_MODIFIED',$MODULE_NAME))}</small> <small title="{Vtiger_Util_Helper::formatDateTimeIntoDayString($COMMENT->getModifiedTime())}" class="commentModifiedTime">{Vtiger_Util_Helper::formatDateDiffInStrings($COMMENT->getModifiedTime())}</small> </p> </span> - {/if} - </div> - <div class="row marginBottom10px"> - <div class="col-lg-12 col-md-12 col-sm-12"> - <p class="text-muted"> - <small> - <span name="editReason" class="wordbreak">{nl2br($REASON_TO_EDIT)}</span> - </small> - </p> </div> - </div> + {/if} + <br> </div> </div> </div> @@ -202,15 +197,15 @@ {if $PAGING_MODEL->isNextPageExists()} <div class="row"> <div class="textAlignCenter"> - <a href="javascript:void(0)" class="moreRecentComments">{vtranslate('LBL_SHOW_MORE',$MODULE_NAME)}</a> + <a href="javascript:void(0)" class="moreRecentComments" style="color: blue;">{vtranslate('LBL_SHOW_MORE',$MODULE_NAME)}</a> </div> </div> {/if} </div> - <div class="hide basicAddCommentBlock container-fluid"> + <div class="hide basicAddCommentBlock container-fluid" style="min-height: 110px;"> <div class="commentTextArea row"> - <textarea name="commentcontent" class="commentcontent" placeholder="{vtranslate('LBL_ADD_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> + <textarea name="commentcontent" class="commentcontent col-lg-12" placeholder="{vtranslate('LBL_ADD_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> </div> <div class="pull-right row"> {if in_array($MODULE_NAME, $PRIVATE_COMMENT_MODULES)} @@ -231,7 +226,7 @@ </div> <div class="row" style="padding-bottom: 10px;"> <div class="commentTextArea"> - <textarea name="commentcontent" class="commentcontenthidden" placeholder="{vtranslate('LBL_ADD_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> + <textarea name="commentcontent" class="commentcontenthidden col-lg-12" placeholder="{vtranslate('LBL_ADD_YOUR_COMMENT_HERE', $MODULE_NAME)}" rows="{$COMMENT_TEXTAREA_DEFAULT_ROWS}"></textarea> </div> </div> <input type="hidden" name="is_private"> diff --git a/layouts/v7/modules/Vtiger/resources/Detail.js b/layouts/v7/modules/Vtiger/resources/Detail.js index 67fe3ed17f66879e5c647154d0756b3912ffeace..f9659017aedcb4ab11b74d828f203bfd17ef69b8 100644 --- a/layouts/v7/modules/Vtiger/resources/Detail.js +++ b/layouts/v7/modules/Vtiger/resources/Detail.js @@ -2106,6 +2106,24 @@ Vtiger.Class("Vtiger_Detail_Js",{ }); }, + toggleCommentContent: function (e) { + var currentTarget = jQuery(e.currentTarget); + var commentContentBlock = currentTarget.closest('.commentInfoContentBlock'); + var commentContentInfo = commentContentBlock.find('.commentInfoContent'); + var toggleElement = jQuery('<div><a class="pull-right toggleComment" style="color: blue;"><small></small></a><div>'); + var fullComment = vtUtils.linkifyStr(commentContentInfo.data('fullcomment')); + + if (currentTarget.hasClass('showMore')) { + toggleElement.find('small').text(commentContentInfo.data('less')); + commentContentInfo.html(fullComment+toggleElement.clone().html()); + } else { + var maxLength = commentContentInfo.data('maxlength'); + toggleElement.find('small').text(commentContentInfo.data('more')); + toggleElement.find('.toggleComment').addClass('showMore'); + commentContentInfo.html(vtUtils.htmlSubstring(fullComment, maxLength)+"..."+toggleElement.clone().html()); + } + }, + toggleRollupComments : function (e) { e.stopPropagation(); e.preventDefault(); @@ -2849,7 +2867,13 @@ Vtiger.Class("Vtiger_Detail_Js",{ var commentInfoContent = commentInfoBlock.find('.commentInfoContent'); var commentReason = commentInfoBlock.find('[name="editReason"]'); var editCommentBlock = self.getEditCommentBlock(); - editCommentBlock.find('.commentcontent').text(commentInfoContent.text()); + var fullComment = commentInfoContent.data('fullcomment'); + if (fullComment) { + fullComment = app.helper.getDecodedValue(fullComment); + } else { + fullComment = commentInfoContent.text(); + } + editCommentBlock.find('.commentcontent').text(fullComment); editCommentBlock.find('[name="reasonToEdit"]').val(commentReason.text()); editCommentBlock.find('[name="is_private"]').val(commentInfoBlock.find('[name="is_private"]').val()); /*commentInfoContent.hide(); @@ -2886,7 +2910,7 @@ Vtiger.Class("Vtiger_Detail_Js",{ app.request.post({data: params}).then( function(err, data) { if (data) { - commentArea = commentInfoBlock.find('.commentcontent'); + var commentArea = commentInfoBlock.find('.commentcontent'); commentArea.val(data.usersString); commentArea.focus(); var strLength= commentArea.val().length * 2; @@ -2910,6 +2934,10 @@ Vtiger.Class("Vtiger_Detail_Js",{ recentDocumentsTab.trigger('click'); }); + detailContentsHolder.off('.toggleComment').on('click', '.toggleComment', function (e) { + self.toggleCommentContent(e); + }); + app.event.on('post.summarywidget.load',function(event,widgetContainer){ vtUtils.applyFieldElementsView(widgetContainer); diff --git a/layouts/v7/modules/Vtiger/resources/Utils.js b/layouts/v7/modules/Vtiger/resources/Utils.js index 6332b26ed71c00ec67370d61cd7ae88e3fff8b8f..452fc4c19527f773512899a3cae485afe247b4b9 100644 --- a/layouts/v7/modules/Vtiger/resources/Utils.js +++ b/layouts/v7/modules/Vtiger/resources/Utils.js @@ -1,3 +1,11 @@ +/*+********************************************************************************** + * The contents of this file are subject to the vtiger CRM Public License Version 1.1 + * ("License"); You may not use this file except in compliance with the License + * The Original Code is: vtiger CRM Open Source + * The Initial Developer of the Original Code is vtiger. + * Portions created by vtiger are Copyright (C) vtiger. + * All Rights Reserved. + ************************************************************************************/ var vtUtils = { @@ -240,6 +248,51 @@ var vtUtils = { element.trigger('Vtiger.Qtip.HideMesssage'); }, + linkifyStr : function(str) { + var options = {'TLDs':267}; + return anchorme.js(str,options); + }, + + htmlSubstring : function(content, maxlength) { + var m, r = /<([^>\s]*)[^>]*>/g, + stack = [], + lasti = 0, + result = ''; + + //for each tag, while we don't have enough characters + while ((m = r.exec(content)) && maxlength) { + //get the text substring between the last tag and this one + var temp = content.substring(lasti, m.index).substr(0, maxlength); + //append to the result and count the number of characters added + result += temp; + maxlength -= temp.length; + lasti = r.lastIndex; + + if (content) { + result += m[0]; + if (m[1].indexOf('/') === 0) { + //if this is a closing tag, then pop the stack (does not account for bad html) + stack.pop(); + } else if (m[1].lastIndexOf('/') !== m[1].length - 1) { + //if this is not a self closing tag then push it in the stack + stack.push(m[1]); + } + } + } + + //add the remainder of the string, if needed (there are no more tags in here) + result += content.substr(lasti, maxlength); + + //fix the unclosed tags + while (stack.length) { + var unclosedtag = stack.pop(); + if(jQuery.inArray(unclosedtag,['br']) == -1){ + result += '</' + unclosedtag + '>'; + } + } + return result; + }, + showValidationMessage : function(element,message,params) { if(element.hasClass('select2')) { element = app.helper.getSelect2FromSelect(element);