From 8151738d91a3ad464f5c57fc4727e460d44cbf2c Mon Sep 17 00:00:00 2001 From: imtodor Date: Tue, 25 Feb 2020 18:51:59 +0200 Subject: [PATCH 1/4] feat: Copy code snippet button added. --- _assets/css/_styles.scss | 21 +++++++++++ _assets/js/clipboard.min.js | 6 +-- _assets/js/code-snippet.js | 74 ++++++++++++++++++++++++++++++------- _assets/js/main.js | 1 + 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/_assets/css/_styles.scss b/_assets/css/_styles.scss index deb41ba..76366a4 100644 --- a/_assets/css/_styles.scss +++ b/_assets/css/_styles.scss @@ -4630,3 +4630,24 @@ div#sidetoc { display: none; } } + +div{ + &.k-content { + &.k-state-active{ + .copy-code-btn { + padding:7px 10px; + } + } + } +} +.copy-code-btn { + position: absolute; + top: 0; + right: 0; + padding:10px; + z-index: 100; + + &:hover { + cursor: pointer; + } +} diff --git a/_assets/js/clipboard.min.js b/_assets/js/clipboard.min.js index 580433f..0937701 100644 --- a/_assets/js/clipboard.min.js +++ b/_assets/js/clipboard.min.js @@ -1,7 +1,7 @@ /*! - * clipboard.js v1.5.12 + * clipboard.js v2.0.0 * https://zenorocha.github.io/clipboard.js - * + * * Licensed MIT © Zeno Rocha */ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,o){function i(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(r)return r(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=n[a]={exports:{}};e[a][0].call(u.exports,function(t){var n=e[a][1][t];return i(n?n:t)},u,u.exports,t,e,n,o)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;ao;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],i=[];if(o&&e)for(var r=0,a=o.length;a>r;r++)o[r].fn!==e&&o[r].fn._!==e&&i.push(o[r]);return i.length?n[t]=i:delete n[t],this}},e.exports=o},{}],8:[function(e,n,o){!function(i,r){if("function"==typeof t&&t.amd)t(["module","select"],r);else if("undefined"!=typeof o)r(n,e("select"));else{var a={exports:{}};r(a,i.select),i.clipboardAction=a.exports}}(this,function(t,e){"use strict";function n(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var i=n(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol?"symbol":typeof t},a=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); diff --git a/_assets/js/code-snippet.js b/_assets/js/code-snippet.js index 66e4712..9ec82b3 100644 --- a/_assets/js/code-snippet.js +++ b/_assets/js/code-snippet.js @@ -1,23 +1,70 @@ var hasDataLang = false; +var clipboard; const selectedLanguageKey = "Selected_TabStrip_Language_Key"; // Necessary for the offline docs. const localStorageMock = { - getItem: function() { + getItem: function () { return null; }, - setItem: function() { + setItem: function () { } }; +function usesClipboardJs() { + return window.ClipboardJS && !/\[native code\]/.test(window.ClipboardJS.toString()); +}; + +function setTooltip(btn, message) { + $(btn).tooltip('hide') + .attr('data-original-title', message) + .tooltip('show'); +}; + +function hideTooltip(btn) { + setTimeout(function () { + $(btn).tooltip('hide'); + }, 1000); +}; + +function addCopyButton(element) { + $(element.parentNode.className.indexOf("k-content") >= 0 ? $(element).parent() : element) + .prepend(''); + + if (usesClipboardJs()) { + if (clipboard) { + clipboard.destroy(); + } + clipboard = new ClipboardJS('.copy-code-btn', { + text: function () { + return $(element).text(); + } + }); + + clipboard.on('success', function (e) { + setTooltip(e.trigger, 'Copied!'); + hideTooltip(e.trigger); + }); + + $('.copy-code-btn').tooltip({ + container: 'body', + trigger: 'manual', + placement: 'top', + title: 'Copy Code' + }); + } +} + function handleDataLangCodeSnippets() { - $("pre[data-lang]").each(function() { + $("pre[data-lang]").each(function () { + addCopyButton(this); + if (this.parentNode.className.indexOf("k-content") >= 0) { return; } var langs = $(this).nextUntil(":not(pre)", "pre").add(this); - var tabs = $.map(langs, function(item) { + var tabs = $.map(langs, function (item) { var title = $(item).attr("data-lang").replace("tab-", ""); return $("
  • ").text(title); }); @@ -29,9 +76,9 @@ function handleDataLangCodeSnippets() { tabs[0].addClass("k-state-active"); var tabstrip = $("
    ") - .insertBefore(this) - .append($("
      ").append(tabs)) - .append(langs); + .insertBefore(this) + .append($("
        ").append(tabs)) + .append(langs); langs.wrap("
        "); @@ -43,11 +90,11 @@ function handleDataLangCodeSnippets() { $(function () { $("pre").addClass("prettyprint"); - + function getStorage() { return localStorage !== undefined ? localStorage : localStorageMock; } - + function saveLanguage(language) { getStorage().setItem(selectedLanguageKey, language); } @@ -112,16 +159,17 @@ $(function () { 'ASPNET': 'html', 'XML': 'xml', 'TypeScript': 'commonjs', - 'C++' : 'cpp', - 'C' : 'c', - 'Objective-C' : 'm', - 'Java' : 'java' + 'C++': 'cpp', + 'C': 'c', + 'Objective-C': 'm', + 'Java': 'java' }; if (hasDataLang) { handleDataLangCodeSnippets(); } else { $("pre").each(function (index) { + addCopyButton(this); var langExtension = codeSampleMapper[$(this).attr('lang')]; $(this).addClass('lang-' + langExtension).addClass("prettyprint"); }); diff --git a/_assets/js/main.js b/_assets/js/main.js index 4cec22e..44c4697 100644 --- a/_assets/js/main.js +++ b/_assets/js/main.js @@ -1,6 +1,7 @@ //= require jquery.min //= require bootstrap.min //= require prettify +//= require clipboard.min //= require kendo/kendo.core.min //= require kendo/kendo.data.min From 961645cc221bd1108f394ddd68d172accd54f9ee Mon Sep 17 00:00:00 2001 From: imtodor Date: Wed, 26 Feb 2020 11:49:01 +0200 Subject: [PATCH 2/4] fix: Tooltip on copy button on hover --- _assets/js/code-snippet.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/_assets/js/code-snippet.js b/_assets/js/code-snippet.js index 9ec82b3..ecd68d9 100644 --- a/_assets/js/code-snippet.js +++ b/_assets/js/code-snippet.js @@ -15,26 +15,25 @@ function usesClipboardJs() { }; function setTooltip(btn, message) { - $(btn).tooltip('hide') - .attr('data-original-title', message) - .tooltip('show'); + $(btn).attr('data-original-title', message) + .tooltip('show'); }; function hideTooltip(btn) { - setTimeout(function () { - $(btn).tooltip('hide'); - }, 1000); + $(btn).tooltip('hide'); }; function addCopyButton(element) { - $(element.parentNode.className.indexOf("k-content") >= 0 ? $(element).parent() : element) + var isCopyButtonOutsideCode = element.parentNode.className.indexOf("k-content") >= 0; + $(isCopyButtonOutsideCode ? $(element).parent() : element) .prepend(''); if (usesClipboardJs()) { if (clipboard) { clipboard.destroy(); } - clipboard = new ClipboardJS('.copy-code-btn', { + var copyButtonSelector = '.copy-code-btn'; + clipboard = new ClipboardJS(copyButtonSelector, { text: function () { return $(element).text(); } @@ -42,21 +41,28 @@ function addCopyButton(element) { clipboard.on('success', function (e) { setTooltip(e.trigger, 'Copied!'); - hideTooltip(e.trigger); + setTimeout(function () { + hideTooltip(e.trigger); + }, 1000); }); - $('.copy-code-btn').tooltip({ + var copyButton = isCopyButtonOutsideCode ? $(element).prev(copyButtonSelector) : $(element).children(copyButtonSelector); + $(copyButton).hover(function (e) { + setTooltip(e.target, 'Copy code') + }, function (e) { + hideTooltip(e.target); + }); + $(copyButton).tooltip({ container: 'body', trigger: 'manual', placement: 'top', - title: 'Copy Code' + title: 'Copy code' }); } } function handleDataLangCodeSnippets() { $("pre[data-lang]").each(function () { - addCopyButton(this); if (this.parentNode.className.indexOf("k-content") >= 0) { return; @@ -169,12 +175,16 @@ $(function () { handleDataLangCodeSnippets(); } else { $("pre").each(function (index) { - addCopyButton(this); var langExtension = codeSampleMapper[$(this).attr('lang')]; $(this).addClass('lang-' + langExtension).addClass("prettyprint"); }); } + $("pre").each(function () { + addCopyButton(this); + }); + + prettyPrint(); /* END TabStrip logic */ From dc7834606a2b0a3402a7837943fad7c9387bbfdb Mon Sep 17 00:00:00 2001 From: imtodor Date: Fri, 28 Feb 2020 13:19:58 +0200 Subject: [PATCH 3/4] fix: Copy button copies only the last code snippet --- _assets/js/code-snippet.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/_assets/js/code-snippet.js b/_assets/js/code-snippet.js index ecd68d9..1d0fbd6 100644 --- a/_assets/js/code-snippet.js +++ b/_assets/js/code-snippet.js @@ -23,17 +23,18 @@ function hideTooltip(btn) { $(btn).tooltip('hide'); }; -function addCopyButton(element) { + +function addCopyButton(element, index) { var isCopyButtonOutsideCode = element.parentNode.className.indexOf("k-content") >= 0; $(isCopyButtonOutsideCode ? $(element).parent() : element) .prepend(''); if (usesClipboardJs()) { - if (clipboard) { - clipboard.destroy(); - } var copyButtonSelector = '.copy-code-btn'; - clipboard = new ClipboardJS(copyButtonSelector, { + var id = `${copyButtonSelector.slice(1)}-${index}`; + var copyButton = isCopyButtonOutsideCode ? $(element).prev(copyButtonSelector) : $(element).children(copyButtonSelector); + $(copyButton).attr('id', id) + clipboard = new ClipboardJS(`#${id}`, { text: function () { return $(element).text(); } @@ -46,7 +47,6 @@ function addCopyButton(element) { }, 1000); }); - var copyButton = isCopyButtonOutsideCode ? $(element).prev(copyButtonSelector) : $(element).children(copyButtonSelector); $(copyButton).hover(function (e) { setTooltip(e.target, 'Copy code') }, function (e) { @@ -180,8 +180,8 @@ $(function () { }); } - $("pre").each(function () { - addCopyButton(this); + $("pre").each(function (index) { + addCopyButton(this, index); }); From 55a2c20afa413f7792cef4bd32b74d697b2a6029 Mon Sep 17 00:00:00 2001 From: imtodor Date: Fri, 28 Feb 2020 13:49:09 +0200 Subject: [PATCH 4/4] fix: Changed ES6 string format --- _assets/js/code-snippet.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_assets/js/code-snippet.js b/_assets/js/code-snippet.js index 1d0fbd6..717b003 100644 --- a/_assets/js/code-snippet.js +++ b/_assets/js/code-snippet.js @@ -16,7 +16,7 @@ function usesClipboardJs() { function setTooltip(btn, message) { $(btn).attr('data-original-title', message) - .tooltip('show'); + .tooltip('show'); }; function hideTooltip(btn) { @@ -31,10 +31,10 @@ function addCopyButton(element, index) { if (usesClipboardJs()) { var copyButtonSelector = '.copy-code-btn'; - var id = `${copyButtonSelector.slice(1)}-${index}`; + var id = copyButtonSelector.slice(1) + "-" + index; var copyButton = isCopyButtonOutsideCode ? $(element).prev(copyButtonSelector) : $(element).children(copyButtonSelector); $(copyButton).attr('id', id) - clipboard = new ClipboardJS(`#${id}`, { + clipboard = new ClipboardJS("#" + id, { text: function () { return $(element).text(); }