diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index 0e873d5..d3de6fa 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -16,7 +16,7 @@ jobs: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 mariadb: - image: mariadb:10.5 + image: mariadb:10.6.7 env: MYSQL_USER: 'root' MYSQL_ALLOW_EMPTY_PASSWORD: "true" @@ -28,21 +28,15 @@ jobs: fail-fast: false matrix: include: - - php: '8.0' - moodle-branch: 'MOODLE_400_STABLE' + - php: '8.2' + moodle-branch: 'MOODLE_403_STABLE' database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_311_STABLE' + - php: '8.1' + moodle-branch: 'MOODLE_402_STABLE' database: 'mariadb' - - php: '7.4' - moodle-branch: 'MOODLE_311_STABLE' - database: 'pgsql' - - php: '7.4' - moodle-branch: 'MOODLE_310_STABLE' + - php: '8.0' + moodle-branch: 'MOODLE_401_STABLE' database: 'pgsql' - - php: '7.3' - moodle-branch: 'MOODLE_39_STABLE' - database: 'mariadb' steps: - name: Check out repository code diff --git a/amd/build/gradient.min.js b/amd/build/gradient.min.js new file mode 100644 index 0000000..256ff58 --- /dev/null +++ b/amd/build/gradient.min.js @@ -0,0 +1,3 @@ +define("block_dash/gradient",["block_dash/lc_color_picker"],(function(Picker){return Picker})); + +//# sourceMappingURL=gradient.min.js.map \ No newline at end of file diff --git a/amd/build/gradient.min.js.map b/amd/build/gradient.min.js.map new file mode 100644 index 0000000..4dee205 --- /dev/null +++ b/amd/build/gradient.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"gradient.min.js","sources":["../src/gradient.js"],"sourcesContent":["define(['block_dash/lc_color_picker'], function(Picker) {\n return Picker;\n});"],"names":["define","Picker"],"mappings":"AAAAA,6BAAO,CAAC,+BAA+B,SAASC,eACrCA"} \ No newline at end of file diff --git a/amd/build/gradienthandler.min.js b/amd/build/gradienthandler.min.js new file mode 100644 index 0000000..cfc8a90 --- /dev/null +++ b/amd/build/gradienthandler.min.js @@ -0,0 +1,3 @@ +define("block_dash/gradienthandler",[],(function(){return{init:function(){alert("demo")}}})); + +//# sourceMappingURL=gradienthandler.min.js.map \ No newline at end of file diff --git a/amd/build/gradienthandler.min.js.map b/amd/build/gradienthandler.min.js.map new file mode 100644 index 0000000..44a5320 --- /dev/null +++ b/amd/build/gradienthandler.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"gradienthandler.min.js","sources":["../src/gradienthandler.js"],"sourcesContent":["define([], function () {\n return {\n init: function() {\n alert('demo');\n }\n };\n});\n"],"names":["define","init","alert"],"mappings":"AAAAA,oCAAO,IAAI,iBACA,CACHC,KAAM,WACFC,MAAM"} \ No newline at end of file diff --git a/amd/build/lc_color_picker.min.js b/amd/build/lc_color_picker.min.js new file mode 100644 index 0000000..c7fe032 --- /dev/null +++ b/amd/build/lc_color_picker.min.js @@ -0,0 +1,3 @@ +var global,factory;global=window,factory=function(){if(void 0!==window.lc_color_picker)return!1;let debounced_vars=[],window_width=null,style_generated=null,active_trigger=null,active_trig_id=null,active_solid=null,active_opacity=null,active_gradient=null,active_mode="linear-gradient",sel_grad_step=0,gradient_data={deg:0,radial_circle:!1,steps:[]};const def_opts={modes:["linear-gradient"],open_on_focus:!0,transparency:!0,dark_theme:!1,no_input_mode:!1,wrap_width:"auto",preview_style:{input_padding:35,side:"right",width:30,separator_color:"#ccc"},fallback_colors:["#008080","linear-gradient(90deg, #fff 0%, #000 100%)"],on_change:null,labels:["click to change color","Solid","Linear Gradient","Radial Gradient","add gradient step","gradient angle","gradient shape","color","opacity"]},right_input_selector='input:not([type="color"])',lccp_ivc_event=function(picker_id){let hide_picker=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return new CustomEvent("lccp_input_val_check",{bubbles:!0,detail:{picker_id:picker_id,hide_picker:hide_picker}})};document.addEventListener("click",(function(e){const picker=document.querySelector("#lc-color-picker.lccp-shown");if(!picker||e.target.classList.contains("lccp-preview"))return!0;for(const trigger of document.getElementsByClassName("lccp-preview"))if(trigger.contains(e.target))return!0;if(e.target.parentNode&&e.target.parentNode.classList&&e.target.parentNode.classList.contains("lccp-el-wrap")&&document.getElementById(active_trig_id))return!0;if(!picker.contains(e.target)&&!e.target.classList.contains("lccp-shown")){const picker_id=picker.getAttribute("data-trigger-id");document.getElementById(picker_id).parentNode.querySelector(right_input_selector).dispatchEvent(lccp_ivc_event(picker_id,!0))}return!0})),window.addEventListener("resize",(function(e){const picker=document.querySelector("#lc-color-picker.lccp-shown");if(!picker||window_width==window.innerWidth)return!0;const picker_id=picker.getAttribute("data-trigger-id");document.getElementById(picker_id).parentNode.querySelector(right_input_selector).dispatchEvent(lccp_ivc_event(picker_id,!0))})),String.prototype.lccpReplaceArray=function(find,replace){let replaceString=this;for(var i=0;i1&&void 0!==arguments[1]?arguments[1]:{};if(this.attachTo=attachTo,!this.attachTo)return console.error("You must provide a valid selector string first argument");if("object"!=typeof options)return console.error("Options must be an object");const bkp_opts=options;options=Object.assign({},def_opts,options),void 0!==bkp_opts.preview_style&&(options.preview_style=Object.assign({},def_opts.preview_style,bkp_opts.preview_style)),this.init=function(){const $this=this;style_generated||(this.generate_style(),style_generated=!0),maybe_querySelectorAll(attachTo).forEach((function(el){"INPUT"==el.tagName&&"text"!=el.getAttribute("type")||el.parentNode.classList.length&&el.parentNode.classList.contains("lcslt_wrap")||$this.wrap_element(el)}))},this.wrap_element=function(el){cp_uniqid=Math.random().toString(36).substr(2,9);const $this=this,side_prop="right"==options.preview_style.side?"borderRightWidth":"borderLeftWidth";let trigger_css="width:".concat(options.no_input_mode?"calc(100% - "+parseInt(getComputedStyle(el).borderRightWidth,10)+"px - "+parseInt(getComputedStyle(el).borderLeftWidth,10)+"px);":options.preview_style.width+"px;")+options.preview_style.side+":"+parseInt(getComputedStyle(el)[side_prop],10)+"px;top:"+parseInt(getComputedStyle(el).borderTopWidth,10)+"px;height: calc(100% - "+parseInt(getComputedStyle(el).borderTopWidth,10)+"px - "+parseInt(getComputedStyle(el).borderBottomWidth,10)+"px);",trigger_upper_css=trigger_css+"background:"+el.value+";border-color:"+options.preview_style.separator_color+";",div=document.createElement("div");div.className="lccp-preview-"+options.preview_style.side,div.setAttribute("data-for",el.getAttribute("name")),"auto"!=options.wrap_width&&(div.style.width="inherit"==options.wrap_width?Math.round(el.getBoundingClientRect().width)+"px":options.wrap_width);const direct_colorpicker_code=options.transparency||1!=options.modes.length||"linear-gradient"!=options.modes[0]?"":'';div.classList.add("lccp-el-wrap"),div.innerHTML=''+direct_colorpicker_code,el.parentNode.insertBefore(div,el),div.appendChild(el),options.no_input_mode||("right"==options.preview_style.side?div.querySelector('input:not([type="color"])').style.paddingRight=options.preview_style.input_padding+"px":div.querySelector('input:not([type="color"])').style.paddingLeft=options.preview_style.input_padding+"px"),div.querySelector(".lccp-direct-cp-f")&&div.querySelector(".lccp-direct-cp-f").addEventListener("input",(e=>{div.querySelector('input:not([type="color"])').value=e.target.value,div.querySelector(".lccp-preview").style.background=e.target.value}));const trigger=document.getElementById(cp_uniqid);trigger.addEventListener("click",(e=>{this.show_picker(trigger)})),options.open_on_focus&&div.querySelector(right_input_selector).addEventListener("focus",(e=>{trigger!=active_trigger&&(active_trigger&&(document.getElementById("lc-color-picker").classList.remove("lccp-shown"),active_trigger=null),$this.debounce("open_on_focus",10,"show_picker",trigger))})),div.querySelector(right_input_selector).addEventListener("keyup",(e=>{if(9==e.keyCode||"Enter"===e.key||13===e.keyCode)return;const is_active_trigger_and_opened=!!(active_trig_id=cp_uniqid&&document.querySelector("#lc-color-picker.lccp-shown"));active_trigger=trigger,active_trig_id=cp_uniqid,$this.debounce("manual_input_sync",10,"val_to_picker",!0),is_active_trigger_and_opened&&($this.debounce("manual_input_sync_cp",10,"append_color_picker",!1),$this.debounce("reopen_picker_after_manual_edit",10,"show_picker",trigger))})),div.querySelector(right_input_selector).addEventListener("focusout",(e=>{if("BODY"==document.activeElement.tagName&&document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+active_trig_id+'"]'))return!0;e.target.dispatchEvent(lccp_ivc_event(active_trig_id,!0))})),div.querySelector(right_input_selector).addEventListener("lccp_input_val_check",(e=>{const curr_val=e.target.value,test=document.createElement("div");test.style.background=curr_val;let val_to_set,browser_val=test.style.background;curr_val.trim().length&&browser_val?(browser_val=browser_val.replaceAll("0.",".").replace(/rgb\([^\)]+\)/g,(rgb=>$this.RGB_to_hex(rgb))),val_to_set="rgb("==browser_val.trim().toLowerCase().substr(0,4)?$this.RGB_to_hex(browser_val):browser_val):val_to_set=-1===e.target.value.toLowerCase().indexOf("gradient")?-1===options.fallback_colors[0].toLowerCase().indexOf("rgba")?$this.RGB_to_hex(options.fallback_colors[0]):options.fallback_colors[0]:options.fallback_colors[1],val_to_set!=curr_val&&(e.target.value=val_to_set),"function"==typeof options.on_change&&last_tracked_col!=val_to_set&&options.on_change.call($this,val_to_set,e.target),e.detail.picker_id==active_trig_id&&(active_trigger=null,active_trig_id=null);const $target=document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+e.detail.picker_id+'"]');$target&&($target.classList.remove("lccp-shown"),document.getElementById("lc-color-picker").remove())}))},this.show_picker=function(trigger){if(document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+active_trig_id+'"]'))return document.getElementById("lc-color-picker").remove(),active_trigger=null,active_trig_id=null,!1;const direct_colorpicker=trigger.parentNode.querySelector(".lccp-direct-cp-f");if(direct_colorpicker&&(!options.open_on_focus||options.open_on_focus&&!navigator.userAgent.toLowerCase().includes("firefox")))return direct_colorpicker.value=active_solid,direct_colorpicker.click(),!0;window_width=window.innerWidth,active_trigger=trigger,active_trig_id=cp_uniqid,this.val_to_picker(),this.append_color_picker();const picker=document.getElementById("lc-color-picker"),picker_w=picker.offsetWidth,picker_h=picker.offsetHeight,at_offsety=active_trigger.getBoundingClientRect(),at_h=parseInt(active_trigger.clientHeight,10)+parseInt(getComputedStyle(active_trigger).borderTopWidth,10)+parseInt(getComputedStyle(active_trigger).borderBottomWidth,10),y_pos=parseInt(at_offsety.y,10)+parseInt(window.pageYOffset,10)+at_h+5;let left=parseInt(at_offsety.right,10)-picker_w;left<0&&(left=0),window.innerWidth<700&&(left=Math.floor((window.innerWidth-picker_w)/2));const y_pos_css=y_pos+picker_h-document.documentElement.scrollTop=6&&hex[0]===hex[1]&&hex[2]===hex[3]&&hex[4]===hex[5]?"#"+hex[0]+hex[2]+hex[4]:"#"+hex.join("")},this.short_hex_fix=function(hex){if(4==hex.length){const a=hex.split("");hex=a[0]+a[1]+a[1]+a[2]+a[2]+a[3]+a[3]}return hex.toLowerCase()},this.hex_to_RGB=function(h){let r=0,g=0,b=0;return 4==h.length?(r="0x"+h[1]+h[1],g="0x"+h[2]+h[2],b="0x"+h[3]+h[3]):7==h.length&&(r="0x"+h[1]+h[2],g="0x"+h[3]+h[4],b="0x"+h[5]+h[6]),"rgb("+ +r+", "+ +g+", "+ +b+")"},this.hex_to_RGBA=function(h,opacity){return 1===parseFloat(opacity)?this.shorten_hex(h):this.hex_to_RGB(h).replace("(","a(").replace(")",", "+opacity.toString().replace("0.",".")+")")},this.append_color_picker=function(){let on_manual_input_change=arguments.length>0&&void 0!==arguments[0]&&arguments[0];const $this=this,theme_class=options.dark_theme?"lccp_dark_theme":"lccp_light_theme",shown_solid="solid"==active_mode?active_solid:gradient_data.steps[0].color,shown_opacity="solid"==active_mode?active_opacity:options.transparency?gradient_data.steps[0].opacity:null,print_grad_code=-1!==options.modes.indexOf("linear-gradient")||-1!==options.modes.indexOf("radial-gradient");let picker_el,picker="";if(on_manual_input_change&&document.getElementById("lc-color-picker")?(picker_el=document.getElementById("lc-color-picker"),picker_el.setAttribute("data-mode",active_mode),picker_el.setAttribute("data-trigger-id",cp_uniqid)):picker='
',print_grad_code&&(picker+='\n
\n
\n
\n\n
\n\n
\n angle\n\n \n \n
\n
\n shape\n\n Ellipse\n Circle\n
\n
\n
')),picker+='\n
\n color\n\n
\n \n
\n \n
'),options.transparency&&(picker+='\n
\n opacity\n\n \n \n
')),on_manual_input_change&&document.getElementById("lc-color-picker")?picker_el.innerHTML=picker:document.body.insertAdjacentHTML("beforeend",picker+"
"),options.modes.length>=1)for(const mode of document.querySelectorAll("#lccp_modes_wrap span"))mode.addEventListener("click",(e=>{$this.mode_change(e.target,e.target.getAttribute("data-mode"))}));if(print_grad_code&&(gradient_data.steps.some((function(step,index){$this.add_draggable_element(index,step.position,step.color)})),document.querySelector(".lccp_gradient:not(.lccp_gradient-bg)").addEventListener("click",(e=>{this.add_gradient_step(e)}))),-1!==options.modes.indexOf("linear-gradient")&&(document.querySelector(".pccp_deg_f_wrap input[type=range]").addEventListener("input",(e=>{this.track_deg_range_change(e)})),document.querySelector(".pccp_deg_f_wrap input[name=deg-num]").addEventListener("change",(e=>{this.track_deg_num_change(e)})),document.querySelector(".pccp_deg_f_wrap input[name=deg-num]").addEventListener("keyup",(e=>{this.debounce("deg_f_change",500,"track_deg_num_change",e)}))),-1!==options.modes.indexOf("radial-gradient"))for(const mode of document.querySelectorAll(".pccp_circle_f_wrap span"))mode.addEventListener("click",(e=>{$this.set_ellipse_circle(e.target,e.target.getAttribute("data-val"))}));document.querySelector('.pccp_color_f_wrap input[type="color"]').addEventListener("input",(e=>{this.track_color_change(e)})),document.querySelector('.pccp_color_f_wrap input[type="color"]').addEventListener("change",(e=>{this.track_color_change(e)})),document.querySelector(".pccp_color_f_wrap input[name=hex]").addEventListener("keyup",(e=>{this.debounce("hex_f_change",600,"track_color_hex_change",e)})),options.transparency&&(document.querySelector(".pccp_opacity_f_wrap input[type=range]").addEventListener("input",(e=>{this.track_opacity_range_change(e)})),document.querySelector(".pccp_opacity_f_wrap input[name=opacity-num]").addEventListener("change",(e=>{this.track_opacity_num_change(e)})),document.querySelector(".pccp_opacity_f_wrap input[name=opacity-num]").addEventListener("keyup",(e=>{this.debounce("opacity_f_change",500,"track_opacity_num_change",e)})))},this.add_draggable_element=function(rel_step_num,position,color){const $this=this,container=document.querySelector(".lccp_gradient_ranges"),sel_class=rel_step_num?"":"lccp_sel_step",del_btn_vis=gradient_data.steps.length>2?"":'style="display: none;"';container.innerHTML+='";let active=!1;const dragStart=function(range_id,el,e){active=range_id},dragEnd=function(){active=!1,$this.apply_changes()},drag=function(range_id,range,e){if(!1!==active&&range_id==active){e.preventDefault();const rect=container.getBoundingClientRect();let new_pos="touchmove"===e.type?e.touches[0].clientX-rect.left:e.clientX-rect.left;new_pos=Math.round(100*new_pos/container.offsetWidth),new_pos<0?new_pos=0:new_pos>100&&(new_pos=100);const min_pos=range_id?gradient_data.steps[range_id-1].position:0,max_pos=range_id==gradient_data.steps.length-1?100:gradient_data.steps[range_id+1].position;new_posmax_pos&&(new_pos=max_pos-1),gradient_data.steps[range_id].position=new_pos,range.style.left=new_pos+"%",$this.apply_gradient_changes()}};document.querySelectorAll(".lccp_gradient_range").forEach((range=>{const step_num=parseInt(range.getAttribute("data-step-num"),10);range.removeEventListener("touchstart",null),range.removeEventListener("touchend",null),range.removeEventListener("touchmove",null),range.removeEventListener("click",null),range.removeEventListener("mousedown",null),range.removeEventListener("mouseup",null),range.addEventListener("touchstart",(e=>{dragStart(step_num,e.target)})),range.addEventListener("mousedown",(e=>{dragStart(step_num,e.target)})),range.addEventListener("click",(e=>{$this.select_gradient_color(step_num)})),container.addEventListener("touchmove",(e=>{drag(step_num,range,e)})),container.addEventListener("mousemove",(e=>{drag(step_num,range,e)})),range.addEventListener("mouseup",(e=>{dragEnd()})),range.addEventListener("touchend",(e=>{dragEnd()})),document.addEventListener("mouseup",(e=>{dragEnd()}))})),document.querySelectorAll(".lccp_gradient_range img").forEach((btn=>{btn.addEventListener("click",(e=>{if(document.querySelectorAll(".lccp_gradient_range").length<3)return!1;setTimeout((()=>{const parent=e.target.parentNode,step_num=parseInt(parent.getAttribute("data-step-num"),10),to_select=step_num?step_num-1:0;gradient_data.steps.splice(step_num,1),document.querySelectorAll(".lccp_gradient_range").forEach((r=>r.remove())),gradient_data.steps.some((function(step,index){$this.add_draggable_element(index,step.position,step.color)})),document.querySelector('.lccp_gradient_range[data-step-num="'+to_select+'"]').click(),this.apply_gradient_changes(!0)}),20)}))}))},this.select_gradient_color=function(step_num){sel_grad_step=step_num,document.querySelectorAll(".lccp_gradient_range").forEach((m=>m.classList.remove("lccp_sel_step"))),document.querySelector('.lccp_gradient_range[data-step-num="'+step_num+'"]').classList.add("lccp_sel_step"),active_solid=gradient_data.steps[step_num].color,active_opacity=gradient_data.steps[step_num].opacity,document.querySelector('#lc-color-picker input[type="color"]').value=active_solid,document.querySelector(".pccp_color_f_wrap input[name=hex]").value=active_solid,options.transparency&&(document.querySelector(".pccp_opacity_f_wrap input[type=range]").value=active_opacity,document.querySelector(".pccp_opacity_f_wrap input[name=opacity-num]").value=active_opacity)},this.apply_gradient_changes=function(also_apply_changes){const $this=this;let new_gradient=active_mode+"(";new_gradient+="linear-gradient"==active_mode?gradient_data.deg+"deg":gradient_data.radial_circle?"circle":"ellipse",new_gradient+=", ";let colors_part=[];gradient_data.steps.some((function(step,index){let to_add=options.transparency?$this.hex_to_RGBA(step.color,step.opacity):$this.shorten_hex(step.color);(gradient_data.steps.length>2||gradient_data.steps.length<=2&&(!index&&parseInt(step.position,10)||index&&index{options.on_change.call(this,val,field)}),300)))},this.mode_change=function(el,new_mode){if(active_mode==new_mode)return!1;let color,opacity;"solid"==new_mode?(color=active_solid,options.transparency&&(opacity=active_opacity)):(color=gradient_data.steps[0].color,options.transparency&&(opacity=gradient_data.steps[0].opacity)),document.querySelector('#lc-color-picker input[type="color"]').value=color,document.querySelector(".pccp_color_f_wrap input[name=hex]").value=color,options.transparency&&(document.querySelector(".pccp_opacity_f_wrap input[type=range]").value=opacity,document.querySelector(".pccp_opacity_f_wrap input[name=opacity-num]").value=opacity),options.modes.length>=1&&(document.querySelector(".pccp_deg_f_wrap").style.display="linear-gradient"==new_mode?"flex":"none",document.querySelector(".pccp_circle_f_wrap").style.display="radial-gradient"==new_mode?"block":"none"),-1===options.modes.indexOf("linear-gradient")&&-1===options.modes.indexOf("radial-gradient")||(document.querySelector(".lccp_gradient_wizard").style.display="solid"!=new_mode?"block":"none"),document.querySelectorAll("#lccp_modes_wrap span").forEach((m=>m.classList.remove("lccp_sel_mode"))),el.classList.add("lccp_sel_mode"),active_mode=new_mode,"solid"==new_mode?this.apply_changes():this.apply_gradient_changes(!0)},this.add_gradient_step=function(e){const $this=this,pos=Math.round(100*e.layerX/e.target.offsetWidth);let index=0;for(let step of gradient_data.steps){if(step.position>pos){const step_data={color:index-1<0?step.color:gradient_data.steps[index-1].color,opacity:1,position:pos};gradient_data.steps.splice(index,0,step_data);break}index++}document.querySelectorAll(".lccp_gradient_range").forEach((r=>r.remove())),gradient_data.steps.some((function(step,index){$this.add_draggable_element(index,step.position,step.color)})),document.querySelector('.lccp_gradient_range[data-step-num="'+index+'"]').click(),this.apply_gradient_changes(!0)},this.set_ellipse_circle=function(el,new_opt){if(gradient_data.radial_circle&&"circle"==new_opt||!gradient_data.radial_circle&&"circle"!=new_opt)return!1;gradient_data.radial_circle=!gradient_data.radial_circle,document.querySelectorAll(".pccp_circle_f_wrap span").forEach((m=>m.classList.remove("pcpp_circle_btn_active"))),el.classList.add("pcpp_circle_btn_active"),this.apply_gradient_changes(!0)},this.track_deg_range_change=function(e){document.querySelector(".pccp_deg_f_wrap input[name=deg-num]").value=e.target.value,gradient_data.deg=e.target.value,this.apply_gradient_changes(!0)},this.track_deg_num_change=function(e){let val=parseFloat(e.target.value);(isNaN(val)||val<0||val>360)&&(val=90),e.target.value=val,document.querySelector(".pccp_deg_f_wrap input[type=range]")&&(document.querySelector(".pccp_deg_f_wrap input[type=range]").value=val),gradient_data.deg=val,this.apply_gradient_changes(!0)},this.track_color_change=function(e){const val=e.target.value.toLowerCase();document.querySelector(".pccp_color_f_wrap input[name=hex]").value=val,this.apply_color_change(val)},this.track_color_hex_change=function(e){let val=this.short_hex_fix(e.target.value);null===val.match(/^#[a-f0-9]{6}$/i)&&(val=active_solid.toLowerCase()),e.target.value=val,document.querySelector('#lc-color-picker input[type="color"]').value=val,this.apply_color_change(val)},this.apply_color_change=function(val){"solid"==active_mode?(active_solid=val,this.apply_changes()):(gradient_data.steps[sel_grad_step].color=val,document.querySelector(".lccp_sel_step").style.background=val,this.apply_gradient_changes(!0))},this.track_opacity_range_change=function(e){document.querySelector(".pccp_opacity_f_wrap input[name=opacity-num]").value=e.target.value,this.alter_hex_opacity(e.target.value)},this.track_opacity_num_change=function(e){let val=parseFloat(e.target.value);(isNaN(val)||val<0||val>1)&&(val=.5),e.target.value=val,document.querySelector(".pccp_opacity_f_wrap input[type=range]")&&(document.querySelector(".pccp_opacity_f_wrap input[type=range]").value=val,this.alter_hex_opacity(val))},this.alter_hex_opacity=function(opacity){document.querySelector('#lc-color-picker input[type="color"]').style.opacity=opacity,"solid"==active_mode?(active_opacity=opacity,this.apply_changes()):(gradient_data.steps[sel_grad_step].opacity=opacity,this.apply_gradient_changes(!0))},this.debounce=function(action_name,timing,cb_function,cb_params){void 0!==debounced_vars[action_name]&&debounced_vars[action_name]&&clearTimeout(debounced_vars[action_name]);const $this=this;debounced_vars[action_name]=setTimeout((()=>{$this[cb_function].call($this,cb_params)}),timing)},this.generate_style=function(){document.head.insertAdjacentHTML("beforeend",''))},this.init()};const maybe_querySelectorAll=selector=>{if("string"!=typeof selector){if(selector instanceof Element)return[selector];{let to_return=[];for(const obj of selector)obj instanceof Element&&to_return.push(obj);return to_return}}return(selector.match(/(#[0-9][^\s:,]*)/g)||[]).forEach((function(n){selector=selector.replace(n,'[id="'+n.replace("#","")+'"]')})),document.querySelectorAll(selector)}}(),"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define("block_dash/lc_color_picker",factory):(global="undefined"!=typeof globalThis?globalThis:global||self).Chart=factory(); + +//# sourceMappingURL=lc_color_picker.min.js.map \ No newline at end of file diff --git a/amd/build/lc_color_picker.min.js.map b/amd/build/lc_color_picker.min.js.map new file mode 100644 index 0000000..dae0e0d --- /dev/null +++ b/amd/build/lc_color_picker.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lc_color_picker.min.js","sources":["../src/lc_color_picker.js"],"sourcesContent":["/**\n * lc_color_picker.js - The colorpicker for modern web\n * Version: 2.0.0\n * Author: Luca Montanari (LCweb)\n * Website: https://lcweb.it\n * Licensed under the MIT license\n */\n\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory());\n })(this, (function () { 'use strict';\n\n if(typeof(window.lc_color_picker) != 'undefined') {return false;} // prevent multiple script inits\n\n\n /*** vars ***/\n let debounced_vars = [],\n window_width = null,\n\n style_generated = null,\n active_trigger = null,\n active_trig_id = null,\n\n active_solid = null,\n active_opacity = null,\n active_gradient = null,\n active_mode = 'linear-gradient',\n\n sel_grad_step = 0, // selected gradient step\n gradient_data = {\n deg: 0,\n radial_circle: false,\n steps: [\n //{color : null, opacity: null, position : null}\n ],\n };\n\n\n /*** default options ***/\n const def_opts = {\n modes : ['linear-gradient'], // (array) containing supported modes (solid | linear-gradient | radial-gradient)\n open_on_focus : true, // (bool) whether to open the picker when field is focused\n transparency : true, // (bool) whether to allow colors transparency tune\n dark_theme : false, // (bool) whether to enable dark picker theme\n no_input_mode : false, // (bool) whether to stretch the trigger in order to cover the whole input field\n wrap_width : 'auto', // (string) defines the wrapper width. \"auto\" to leave it up to CSS, \"inherit\" to statically copy input field width, or any other CSS sizing\n preview_style : { // (object) defining shape and position of the in-field preview\n input_padding : 35, // extra px padding eventually added to the target input to not cover text\n side : 'right', // right or left\n width : 30,\n separator_color : '#ccc', // (string) CSS color applird to preview element as separator\n },\n fallback_colors : ['#008080', 'linear-gradient(90deg, #fff 0%, #000 100%)'], // (array) defining default colors used when trigger field has no value. First parameter for solid color, second for gradient\n\n on_change : null, // function(new_value, target_field) {}, - triggered every time field value changes. Passes value and target field object as parameters\n\n labels : [ // (array) option used to translate script texts\n 'click to change color',\n 'Solid',\n 'Linear Gradient',\n 'Radial Gradient',\n 'add gradient step',\n 'gradient angle',\n 'gradient shape',\n 'color',\n 'opacity',\n ],\n };\n\n\n // shortcut var to target the text input only\n const right_input_selector = 'input:not([type=\"color\"])';\n\n\n\n // input value check custom event\n const lccp_ivc_event = function(picker_id, hide_picker = false) {\n return new CustomEvent('lccp_input_val_check', {\n bubbles : true,\n detail: {\n picker_id : picker_id,\n hide_picker : hide_picker\n }\n });\n };\n\n\n\n /*** hide picker cicking outside ***/\n document.addEventListener('click', function(e) {\n const picker = document.querySelector(\"#lc-color-picker.lccp-shown\");\n if(!picker || e.target.classList.contains('lccp-preview')) {\n return true;\n }\n\n // is an element within a trigger?\n for (const trigger of document.getElementsByClassName('lccp-preview')) {\n if(trigger.contains(e.target)) {\n return true;\n }\n }\n\n // clicked on the same colorpicker field? keep visible\n if(e.target.parentNode && e.target.parentNode.classList && e.target.parentNode.classList.contains('lccp-el-wrap') && document.getElementById(active_trig_id)) {\n return true;\n }\n\n // close if clicked element is not in the picker\n if(!picker.contains(e.target) && !e.target.classList.contains('lccp-shown')) {\n const picker_id = picker.getAttribute('data-trigger-id'),\n $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector);\n\n $input.dispatchEvent(lccp_ivc_event(picker_id, true));\n }\n return true;\n });\n\n\n /* hide picker on screen resizing */\n window.addEventListener('resize', function(e) {\n const picker = document.querySelector(\"#lc-color-picker.lccp-shown\");\n if(!picker || window_width == window.innerWidth) {\n return true;\n }\n\n // check field value\n const picker_id = picker.getAttribute('data-trigger-id'),\n $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector);\n\n $input.dispatchEvent(lccp_ivc_event(picker_id, true));\n });\n\n\n /* extend string object to ReplaceArray */\n String.prototype.lccpReplaceArray = function(find, replace) {\n let replaceString = this;\n let regex;\n\n for (var i = 0; i < find.length; i++) {\n const regex = new RegExp(find[i], \"g\");\n replaceString = (typeof(replace) == 'object') ? replaceString.replace(regex, replace[i]) : replaceString.replace(regex, replace);\n }\n return replaceString;\n };\n\n\n\n\n /*** plugin class ***/\n window.lc_color_picker = function(attachTo, options = {}) {\n let cp_uniqid, // unique ID assigned to this colorpicker instance\n last_tracked_col;\n\n this.attachTo = attachTo;\n if(!this.attachTo) {\n return console.error('You must provide a valid selector string first argument');\n }\n\n // override options\n if(typeof(options) != 'object') {\n return console.error('Options must be an object');\n }\n\n const bkp_opts = options;\n options = Object.assign({}, def_opts, options);\n\n if(typeof(bkp_opts.preview_style) != 'undefined') {\n options.preview_style = Object.assign({}, def_opts.preview_style, bkp_opts.preview_style);\n }\n\n\n\n /* initialize */\n this.init = function() {\n const $this = this;\n\n // Generate style\n if(!style_generated) {\n this.generate_style();\n style_generated = true;\n }\n\n\n // assign to each target element\n maybe_querySelectorAll(attachTo).forEach(function(el) {\n if(el.tagName == 'INPUT' && el.getAttribute('type') != 'text') {\n return;\n }\n\n // do not initialize twice\n if(el.parentNode.classList.length && el.parentNode.classList.contains('lcslt_wrap')) {\n return;\n }\n\n $this.wrap_element(el);\n });\n };\n\n\n\n /* wrap target element to allow trigger display */\n this.wrap_element = function(el) {\n cp_uniqid = Math.random().toString(36).substr(2, 9);\n\n const $this = this,\n side_prop = (options.preview_style.side == 'right') ? 'borderRightWidth' : 'borderLeftWidth';\n\n let trigger_css =\n `width:${ (options.no_input_mode) ? 'calc(100% - '+ parseInt(getComputedStyle(el)['borderRightWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderLeftWidth'], 10) +'px);' : options.preview_style.width +'px;'}` +\n\n options.preview_style.side +':'+ parseInt(getComputedStyle(el)[side_prop], 10) +'px;'+\n\n 'top:'+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px;' +\n\n 'height: calc(100% - '+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderBottomWidth'], 10) +'px);';\n\n let trigger_upper_css =\n trigger_css +\n 'background:'+ el.value +';' +\n 'border-color:'+ options.preview_style.separator_color +';'\n\n let div = document.createElement('div');\n div.className = 'lccp-preview-'+ options.preview_style.side;\n div.setAttribute('data-for', el.getAttribute('name'));\n\n // static width from input?\n if(options.wrap_width != 'auto') {\n div.style.width = (options.wrap_width == 'inherit') ? Math.round(el.getBoundingClientRect().width) + 'px' : options.wrap_width;\n }\n\n const direct_colorpicker_code = (!options.transparency && options.modes.length == 1 && options.modes[0] == 'linear-gradient') ?\n '' : '';\n\n div.classList.add(\"lccp-el-wrap\");\n div.innerHTML =\n '' +\n '' +\n direct_colorpicker_code;\n\n el.parentNode.insertBefore(div, el);\n div.appendChild(el);\n\n // input padding\n if(!options.no_input_mode) {\n if(options.preview_style.side == 'right') {\n div.querySelector('input:not([type=\"color\"])').style.paddingRight = options.preview_style.input_padding +'px';\n } else {\n div.querySelector('input:not([type=\"color\"])').style.paddingLeft = options.preview_style.input_padding +'px';\n }\n }\n\n\n // direct browser colorpicker? track changes\n if(div.querySelector('.lccp-direct-cp-f')) {\n div.querySelector('.lccp-direct-cp-f').addEventListener(\"input\", (e) => {\n\n div.querySelector('input:not([type=\"color\"])').value = e.target.value;\n div.querySelector('.lccp-preview').style.background = e.target.value;\n });\n }\n\n\n // event to show picker\n const trigger = document.getElementById(cp_uniqid);\n trigger.addEventListener(\"click\", (e) => {\n this.show_picker(trigger);\n });\n\n\n\n // show on field focus?\n if(options.open_on_focus) {\n div.querySelector(right_input_selector).addEventListener(\"focus\", (e) => {\n if(trigger != active_trigger) {\n if(active_trigger) {\n document.getElementById('lc-color-picker').classList.remove('lccp-shown');\n active_trigger = null;\n }\n\n $this.debounce('open_on_focus', 10, 'show_picker', trigger);\n }\n });\n }\n\n\n // sync manually-inputed data in the field\n div.querySelector(right_input_selector).addEventListener(\"keyup\", (e) => {\n if(e.keyCode == 9 || e.key === 'Enter' || e.keyCode === 13) {\n return;\n }\n\n const is_active_trigger_and_opened = (active_trig_id = cp_uniqid && document.querySelector(\"#lc-color-picker.lccp-shown\")) ? true : false;\n\n active_trigger = trigger;\n active_trig_id = cp_uniqid;\n\n $this.debounce('manual_input_sync', 10, 'val_to_picker', true);\n\n if(is_active_trigger_and_opened) {\n $this.debounce('manual_input_sync_cp', 10, 'append_color_picker', false);\n $this.debounce('reopen_picker_after_manual_edit', 10, 'show_picker', trigger);\n }\n });\n\n\n // be sure input value is managed on focusout\n div.querySelector(right_input_selector).addEventListener(\"focusout\", (e) => {\n // not if this field's picker is shown and focus is on \"body\"\n if(document.activeElement.tagName == 'BODY' && document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ active_trig_id +'\"]')) {\n return true;\n }\n\n e.target.dispatchEvent(lccp_ivc_event(active_trig_id, true));\n });\n\n\n // custom event - check field validity and eventually use fallback values\n div.querySelector(right_input_selector).addEventListener(\"lccp_input_val_check\", (e) => {\n const curr_val = e.target.value,\n test = document.createElement('div');\n\n test.style.background = curr_val;\n let browser_val = test.style.background,\n val_to_set;\n\n if(!curr_val.trim().length || !browser_val) {\n if(e.target.value.toLowerCase().indexOf('gradient') === -1) {\n val_to_set = (options.fallback_colors[0].toLowerCase().indexOf('rgba') === -1) ? $this.RGB_to_hex(options.fallback_colors[0]) : options.fallback_colors[0];\n }\n else {\n val_to_set = options.fallback_colors[1];\n }\n }\n else {\n // browser already fixes minor things\n browser_val = browser_val.replaceAll('0.', '.').replace(/rgb\\([^\\)]+\\)/g, (rgb) => {\n return $this.RGB_to_hex(rgb);\n });\n\n val_to_set = (browser_val.trim().toLowerCase().substr(0, 4) == 'rgb(') ? $this.RGB_to_hex(browser_val) : browser_val;\n }\n\n if(val_to_set != curr_val) {\n e.target.value = val_to_set;\n }\n\n if(typeof(options.on_change) == 'function' && last_tracked_col != val_to_set) {\n options.on_change.call($this, val_to_set, e.target);\n }\n\n if(e.detail.picker_id == active_trig_id) {\n active_trigger = null;\n active_trig_id = null;\n }\n\n\n // also hide picker?\n const $target = document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ e.detail.picker_id +'\"]');\n if($target) {\n\n $target.classList.remove('lccp-shown');\n document.getElementById(\"lc-color-picker\").remove();\n }\n });\n };\n\n\n\n /* show picker */\n this.show_picker = function(trigger) {\n if(document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ active_trig_id +'\"]')) {\n document.getElementById(\"lc-color-picker\").remove();\n active_trigger = null;\n active_trig_id = null\n\n return false;\n }\n\n // direct colorpicker usage? Not for Firefox is \"show on focus\" is enabled\n const direct_colorpicker = trigger.parentNode.querySelector('.lccp-direct-cp-f');\n if(\n direct_colorpicker &&\n (\n !options.open_on_focus ||\n (options.open_on_focus && !navigator.userAgent.toLowerCase().includes('firefox'))\n )\n ) {\n direct_colorpicker.value = active_solid;\n direct_colorpicker.click();\n return true;\n }\n\n\n window_width = window.innerWidth;\n active_trigger = trigger;\n active_trig_id = cp_uniqid;\n\n this.val_to_picker();\n this.append_color_picker();\n\n const picker = document.getElementById('lc-color-picker'),\n picker_w = picker.offsetWidth,\n picker_h = picker.offsetHeight,\n at_offsety = active_trigger.getBoundingClientRect(),\n at_h = parseInt(active_trigger.clientHeight, 10) + parseInt(getComputedStyle(active_trigger)['borderTopWidth'], 10) + parseInt(getComputedStyle(active_trigger)['borderBottomWidth'], 10),\n y_pos = (parseInt(at_offsety.y, 10) + parseInt(window.pageYOffset, 10) + at_h + 5);\n\n // left pos control - also checking side overflows\n let left = (parseInt(at_offsety.right, 10) - picker_w);\n if(left < 0) {\n left = 0;\n }\n\n // mobile? show it centered\n if(window.innerWidth < 700) {\n left = Math.floor( (window.innerWidth - picker_w) / 2);\n }\n\n // top or bottom ?\n const y_pos_css = (y_pos + picker_h - document.documentElement.scrollTop < window.innerHeight) ?\n 'top:'+ y_pos :\n 'transform: translate3d(0, calc((100% + '+ (active_trigger.offsetHeight + 10) +'px) * -1), 0); top:'+ y_pos;\n\n picker.setAttribute('style', y_pos_css +'px; left: '+ left +'px;');\n picker.classList.add('lccp-shown');\n };\n\n\n\n /* handles input value and prepres data for the picker */\n this.val_to_picker = function(from_manual_input) {\n if(!active_trigger) {\n return false;\n }\n const val = active_trigger.parentNode.querySelector(right_input_selector).value.trim().toLowerCase();\n last_tracked_col = val;\n\n // check validity\n let test = document.createElement('div');\n test.style.background = val;\n\n //// set active colors\n // if no value found\n if(!val.length || !test.style.background.length) {\n active_solid = options.fallback_colors[0];\n active_gradient = options.fallback_colors[1];\n active_mode = 'linear-gradient';\n\n /* if(val.indexOf('linear-gradient') !== -1) {\n }\n else if(val.indexOf('radial-gradient') !== -1) {\n active_mode = 'radial-gradient';\n }\n else {\n active_mode = 'solid';\n } */\n }\n else {\n\n active_mode = 'linear-gradient';\n active_gradient = val;\n // find which value type has been passed\n /* if(val.indexOf('linear-gradient') !== -1) {\n }\n else if(val.indexOf('radial-gradient') !== -1) {\n active_mode = 'radial-gradient';\n }\n else {\n active_mode = 'solid';\n }\n\n if(active_mode == 'solid') {\n active_solid = val;\n active_gradient = options.fallback_colors[1];\n }\n else{\n active_solid = options.fallback_colors[0];\n } */\n }\n active_trigger.style.background = val;\n\n if(!from_manual_input || (from_manual_input && options.open_on_focus)) {\n // elaborate solid color data (color and alpha)\n //this.load_solid_data(active_solid);\n // elaborate gradient data\n if(active_gradient) {\n this.load_gradient_data(active_gradient);\n }\n }\n };\n\n\n\n /* elaborate solid color data (color and alpha) loading into active_solid and active_opacity */\n this.load_solid_data = function(raw_data) {\n active_opacity = 1;\n\n // rgba\n if(raw_data.indexOf('rgba') !== -1) {\n const data = this.RGBA_to_hexA(raw_data);\n active_solid = data[0];\n active_opacity = data[1];\n }\n\n // rgb\n else if(raw_data.indexOf('rgba') !== -1) {\n active_solid = this.RGB_to_hex(raw_data);\n }\n\n // hex\n else {\n active_solid = this.short_hex_fix(raw_data);\n }\n };\n\n\n\n /* elaborate gradient data loading into gradient_data */\n this.load_gradient_data = function(raw_data) {\n const $this = this;\n const is_radial = (raw_data.indexOf('radial-gradient') === -1) ? false : true;\n\n // solve issues with inner RGB|RGBA and turn everything into RGBA\n raw_data = raw_data\n .replace(/,\\./g, ',0.').replace(/ \\./g, ' 0.')\n .replace(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+(?:\\.\\d+)?))?\\)/g, 'rgbaZ($1|$2|$3|$4)')\n .replace(/\\|\\)/g, '|1)');\n\n // names to deg\n raw_data = raw_data\n .replace('top right', '45deg').replace('right top', '45deg')\n .replace('bottom right', '135deg').replace('bottom right', '135deg')\n .replace('top left', '315deg').replace('left top', '315deg')\n .replace('bottom left', '225deg').replace('bottom left', '225deg')\n .replace('right', '90deg').replace('left', '270deg').replace('top', '0deg').replace('bottom', '180deg');\n\n // be sure deg or shape is defined\n if(is_radial && raw_data.indexOf('ellipse') === -1 && raw_data.indexOf('circle') === -1) {\n raw_data.replace('\\\\(', '(ellipse ');\n }\n if(!is_radial && raw_data.indexOf('deg') === -1) {\n raw_data.replace('\\\\(', '(180deg');\n }\n\n // process\n raw_data = raw_data.lccpReplaceArray(\n ['linear-gradient', 'radial-gradient', '', '\\\\(', 'to', '\\\\)'],\n ''\n );\n\n // split steps\n const raw_steps = raw_data.split(',');\n const fallback_multiplier = 100 / raw_steps.length;\n\n gradient_data.steps = [];\n raw_steps.some(function(raw_step, index) {\n\n // direction on first index\n if(!index) {\n if(is_radial) {\n gradient_data.radial_circle = (raw_step.indexOf('circle') === -1) ? false : true;\n } else {\n gradient_data.deg = parseInt(raw_step.replace('deg', ''), 10);\n }\n }\n\n // {color : null, opacity: null, position : null}\n else {\n raw_step = raw_step.trim().split(' ');\n let position = '';\n\n // position\n if(raw_step.length < 2) {\n if(index === 1) {\n position = '0%';\n }\n else if(index == (raw_steps.length - 1)) {\n position = '100%';\n }\n else {\n position = (fallback_multiplier * index) +'%';\n }\n }\n else {\n position = raw_step[1];\n }\n\n // color\n let raw_color = raw_step[0],\n opacity = 1;\n\n // normalize to hex\n if(raw_color.indexOf('rgbaZ') !== -1) {\n const col_arr = $this.RGBA_to_hexA(\n raw_color.replace('rgbaZ', 'rgba').replace(/\\|/g, ',')\n );\n\n raw_color = col_arr[0];\n opacity = col_arr[1];\n }\n\n gradient_data.steps.push({\n color : $this.short_hex_fix(raw_color),\n opacity: opacity,\n position : parseInt(position, 10)\n });\n }\n });\n };\n\n\n\n /* handles RGBA string returning a two elements array: hex and alpha */\n this.RGBA_to_hexA = function(raw_data) {\n raw_data = raw_data.lccpReplaceArray(['rgba', '\\\\(', '\\\\)'], '');\n const rgba_arr = raw_data.split(',')\n\n let alpha = (typeof(rgba_arr[3]) != 'undefined') ? rgba_arr[3] : '1';\n if(alpha.substring(0, 1) == '.') {\n alpha = 0 + alpha;\n }\n rgba_arr.splice(3, 1);\n\n return [\n this.RGB_to_hex('rgb('+ rgba_arr.join(',') +')'),\n parseFloat(alpha)\n ];\n };\n\n\n\n /* convert RGB to hex */\n this.RGB_to_hex = function(rgb) {\n rgb = rgb.lccpReplaceArray(['rgb', '\\\\(', '\\\\)'], '');\n const rgb_arr = rgb.split(',');\n\n if(rgb_arr.length < 3) {\n return '';\n }\n\n let r = parseInt(rgb_arr[0].trim(), 10).toString(16),\n g = parseInt(rgb_arr[1].trim(), 10).toString(16),\n b = parseInt(rgb_arr[2].trim(), 10).toString(16);\n\n if (r.length == 1) {r = \"0\" + r;}\n if (g.length == 1) {g = \"0\" + g;}\n if (b.length == 1) {b = \"0\" + b;}\n\n return this.shorten_hex(r + g + b);\n };\n\n\n\n /* if possible, shortenize hex string */\n this.shorten_hex = function(hex) {\n hex = hex.replace('#', '').split('');\n\n if(hex.length >= 6) {\n if(\n hex[0] === hex[1] &&\n hex[2] === hex[3] &&\n hex[4] === hex[5]\n ) {\n return '#'+ hex[0] + hex[2] + hex[4];\n }\n }\n\n return '#'+ hex.join('');\n };\n\n\n\n /* convert short hex to full format */\n this.short_hex_fix = function(hex) {\n if(hex.length == 4) {\n const a = hex.split('');\n hex = a[0] + a[1] + a[1] + a[2] + a[2] + a[3] + a[3];\n }\n\n return hex.toLowerCase();\n };\n\n\n\n /* convert hex to RGB */\n this.hex_to_RGB = function(h) {\n let r = 0, g = 0, b = 0;\n\n // 3 digits\n if (h.length == 4) {\n r = \"0x\" + h[1] + h[1];\n g = \"0x\" + h[2] + h[2];\n b = \"0x\" + h[3] + h[3];\n\n // 6 digits\n } else if (h.length == 7) {\n r = \"0x\" + h[1] + h[2];\n g = \"0x\" + h[3] + h[4];\n b = \"0x\" + h[5] + h[6];\n }\n\n return \"rgb(\"+ +r + \", \" + +g + \", \" + +b + \")\";\n };\n\n\n\n /* convert hex to RGB */\n this.hex_to_RGBA = function(h, opacity) {\n if(parseFloat(opacity) === 1) {\n return this.shorten_hex(h);\n }\n\n let rgb = this.hex_to_RGB(h);\n return rgb.replace('(', 'a(').replace(')', ', '+ opacity.toString().replace('0.', '.') +')');\n };\n\n\n\n\n /* append color container picker to the body */\n this.append_color_picker = function(on_manual_input_change = false) {\n const $this = this;\n\n /* if(document.getElementById(\"lc-color-picker\") && !on_manual_input_change) {\n document.getElementById(\"lc-color-picker\").remove();\n } */\n\n const theme_class = (options.dark_theme) ? 'lccp_dark_theme' : 'lccp_light_theme',\n bg = (active_mode == 'solid') ? active_solid : active_gradient,\n shown_solid = (active_mode == 'solid') ? active_solid : gradient_data.steps[0].color,\n shown_opacity = (active_mode == 'solid') ? active_opacity : (options.transparency) ? gradient_data.steps[0].opacity : null,\n print_grad_code = (options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) ? true : false;\n\n\n // start code\n let picker = '',\n picker_el;\n\n if(on_manual_input_change && document.getElementById(\"lc-color-picker\")) {\n picker_el = document.getElementById(\"lc-color-picker\");\n picker_el.setAttribute('data-mode', active_mode);\n picker_el.setAttribute('data-trigger-id', cp_uniqid);\n }\n else {\n picker = '
';\n }\n\n\n // modes select\n /* if(options.modes.length >= 1) {\n picker += `\n
\n ${ options.labels[1] }\n ${ options.labels[2] }\n ${ options.labels[3] }\n
`;\n } */\n\n\n // gradient wizard\n if(print_grad_code) {\n picker += `\n
\n
\n
\n\n
\n\n
\n \"angle\"\n\n \n \n
\n
\n \"shape\"\n\n Ellipse\n Circle\n
\n
\n
`;\n }\n\n\n // HTML5 colorpicker\n picker += `\n
\n \"color\"\n\n
\n \n
\n \n
`;\n\n // opacity cursor\n if(options.transparency) {\n picker += `\n
\n \"opacity\"\n\n \n \n
`;\n }\n\n\n // append or re-fill\n (on_manual_input_change && document.getElementById(\"lc-color-picker\")) ? picker_el.innerHTML = picker : document.body.insertAdjacentHTML('beforeend', picker +'
');\n\n\n // modes change\n if(options.modes.length >= 1) {\n for (const mode of document.querySelectorAll('#lccp_modes_wrap span')) {\n mode.addEventListener(\"click\", (e) => { $this.mode_change( e.target, e.target.getAttribute('data-mode')) });\n }\n }\n\n // print steps and add gradient step action\n if(print_grad_code) {\n gradient_data.steps.some(function(step, index) {\n $this.add_draggable_element(index, step.position, step.color);\n });\n\n document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').addEventListener(\"click\", (e) => {this.add_gradient_step(e) });\n }\n\n // angle actions\n if(options.modes.indexOf('linear-gradient') !== -1) {\n document.querySelector('.pccp_deg_f_wrap input[type=range]').addEventListener(\"input\", (e) => {this.track_deg_range_change(e)});\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener(\"change\", (e) => {this.track_deg_num_change(e)});\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener(\"keyup\", (e) => {\n this.debounce('deg_f_change', 500, 'track_deg_num_change', e);\n });\n }\n\n // circle actions\n if(options.modes.indexOf('radial-gradient') !== -1) {\n for (const mode of document.querySelectorAll('.pccp_circle_f_wrap span')) {\n mode.addEventListener(\"click\", (e) => { $this.set_ellipse_circle( e.target, e.target.getAttribute('data-val')) });\n }\n }\n\n // color actions\n document.querySelector('.pccp_color_f_wrap input[type=\"color\"]').addEventListener(\"input\", (e) => {this.track_color_change(e)});\n document.querySelector('.pccp_color_f_wrap input[type=\"color\"]').addEventListener(\"change\", (e) => {this.track_color_change(e)});\n document.querySelector('.pccp_color_f_wrap input[name=hex]').addEventListener(\"keyup\", (e) => {\n this.debounce('hex_f_change', 600, 'track_color_hex_change', e);\n });\n\n // transparency actions\n if(options.transparency) {\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').addEventListener(\"input\", (e) => {this.track_opacity_range_change(e)});\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener(\"change\", (e) => {this.track_opacity_num_change(e)});\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener(\"keyup\", (e) => {\n this.debounce('opacity_f_change', 500, 'track_opacity_num_change', e);\n });\n }\n };\n\n\n\n /*** add draggable element ***/\n this.add_draggable_element = function(rel_step_num, position, color) {\n const $this = this,\n container = document.querySelector('.lccp_gradient_ranges'),\n sel_class = (!rel_step_num) ? 'lccp_sel_step' : '',\n del_btn_vis = (gradient_data.steps.length > 2) ? '' : 'style=\"display: none;\"'\n\n container.innerHTML +=\n ''+\n ''+\n '';\n\n let active = false;\n\n //////\n const dragStart = function(range_id, el, e) {\n active = range_id;\n };\n\n const dragEnd = function() {\n active = false;\n $this.apply_changes();\n };\n\n const drag = function(range_id, range, e) {\n if (active !== false && range_id == active) {\n e.preventDefault();\n const rect = container.getBoundingClientRect();\n\n let new_pos = (e.type === \"touchmove\") ? (e.touches[0].clientX - rect.left) : (e.clientX - rect.left);\n new_pos = Math.round((100 * new_pos) / container.offsetWidth);\n\n if(new_pos < 0) {new_pos = 0;}\n else if(new_pos > 100) {new_pos = 100;}\n\n // limit positions basing on previous and next step\n const min_pos = (!range_id) ? 0 : gradient_data.steps[ range_id-1 ].position;\n const max_pos = (range_id == (gradient_data.steps.length - 1)) ? 100 : gradient_data.steps[ range_id+1 ].position;\n\n if(new_pos < min_pos) {new_pos = min_pos + 1;}\n else if(new_pos > max_pos) {new_pos = max_pos - 1;}\n\n gradient_data.steps[ range_id ].position = new_pos;\n range.style.left = new_pos +'%';\n\n $this.apply_gradient_changes();\n }\n };+\n /////\n\n document.querySelectorAll('.lccp_gradient_range').forEach(range => {\n const step_num = parseInt(range.getAttribute('data-step-num'), 10);\n\n range.removeEventListener(\"touchstart\", null);\n range.removeEventListener(\"touchend\", null);\n range.removeEventListener(\"touchmove\", null);\n range.removeEventListener(\"click\", null);\n\n range.removeEventListener(\"mousedown\", null);\n range.removeEventListener(\"mouseup\", null);\n\n range.addEventListener(\"touchstart\", (e) => {dragStart(step_num, e.target, e)});\n range.addEventListener(\"mousedown\", (e) => {dragStart(step_num, e.target, e)});\n\n range.addEventListener(\"click\", (e) => {$this.select_gradient_color(step_num)});\n\n container.addEventListener(\"touchmove\", (e) => {drag(step_num, range, e)});\n container.addEventListener(\"mousemove\", (e) => {drag(step_num, range, e)});\n\n range.addEventListener(\"mouseup\", (e) => {dragEnd()});\n range.addEventListener(\"touchend\", (e) => {dragEnd()});\n document.addEventListener(\"mouseup\", (e) => {dragEnd()});\n });\n\n\n // remove step handler\n document.querySelectorAll('.lccp_gradient_range img').forEach((btn) => {\n\n btn.addEventListener(\"click\", (e) => {\n if(document.querySelectorAll('.lccp_gradient_range').length < 3) {\n return false;\n }\n\n // wait a bit to not interfere with global handler for picker closing\n setTimeout(() => {\n const parent = e.target.parentNode,\n step_num = parseInt(parent.getAttribute('data-step-num'), 10),\n to_select = (!step_num) ? 0 : step_num - 1;\n\n gradient_data.steps.splice(step_num, 1);\n\n // clean and restart\n document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove());\n\n gradient_data.steps.some(function(step, index) {\n $this.add_draggable_element(index, step.position, step.color);\n });\n\n // select newly added element\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ to_select +'\"]').click();\n\n this.apply_gradient_changes(true);\n }, 20);\n });\n });\n };\n\n\n\n /* select gradient color */\n this.select_gradient_color = function(step_num) {\n sel_grad_step = step_num;\n\n document.querySelectorAll('.lccp_gradient_range').forEach(m => m.classList.remove('lccp_sel_step'));\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ step_num +'\"]').classList.add('lccp_sel_step');\n\n active_solid = gradient_data.steps[ step_num ].color;\n active_opacity = gradient_data.steps[ step_num ].opacity;\n\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = active_solid;\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = active_solid;\n\n if(options.transparency) {\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = active_opacity;\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = active_opacity;\n }\n };\n\n\n\n /* apply changes to gradient, after a color/opacity/degree update */\n this.apply_gradient_changes = function(also_apply_changes) {\n const $this = this;\n\n let new_gradient = active_mode+'(';\n new_gradient += (active_mode == 'linear-gradient') ? gradient_data.deg+'deg' : (gradient_data.radial_circle) ? 'circle' : 'ellipse';\n new_gradient += ', ';\n\n let colors_part = []\n gradient_data.steps.some(function(step, index) {\n\n let to_add = (options.transparency) ? $this.hex_to_RGBA(step.color, step.opacity) : $this.shorten_hex(step.color);\n\n if(\n gradient_data.steps.length > 2 ||\n (\n gradient_data.steps.length <= 2 &&\n (\n (!index && parseInt(step.position, 10)) ||\n (index && index < (gradient_data.steps.length - 1)) ||\n (index == (gradient_data.steps.length - 1) && parseInt(step.position, 10) != 100)\n )\n )\n ) {\n to_add += ' '+ step.position +'%';\n }\n\n colors_part.push( to_add );\n });\n\n active_gradient = new_gradient + colors_part.join(', ') + ')';\n\n if(document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)')) {\n document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').style.background = active_gradient;\n }\n\n if(also_apply_changes) {\n this.apply_changes();\n }\n };\n\n\n\n /* apply changes to target field */\n this.apply_changes = function() {\n if(!active_trigger) {\n return false;\n }\n let val = '';\n\n // apply everything to picker global vars\n if(active_mode == 'solid') {\n val = this.shorten_hex(active_solid);\n\n if(options.transparency && document.querySelector('.pccp_opacity_f_wrap input[type=range]')) {\n active_opacity = document.querySelector('.pccp_opacity_f_wrap input[type=range]').value;\n val = this.hex_to_RGBA(val, active_opacity);\n }\n }\n else {\n val = active_gradient;\n }\n\n // apply\n active_trigger.style.background = val;\n\n const field = active_trigger.parentNode.querySelector(right_input_selector),\n old_val = field.value;\n\n if(old_val != val) {\n field.value = val;\n last_tracked_col = val;\n\n if(typeof(options.on_change) == 'function') {\n\n if(typeof(debounced_vars['on_change_cb']) != undefined && debounced_vars['on_change_cb']) {\n clearTimeout(debounced_vars['on_change_cb']);\n }\n debounced_vars['on_change_cb'] = setTimeout(() => {\n options.on_change.call(this, val, field);\n }, 300);\n }\n }\n };\n\n\n\n\n\n\n // HANDLERS\n\n // fields toggle basing on modes change\n this.mode_change = function(el, new_mode) {\n if(active_mode == new_mode) {\n return false;\n }\n let color, opacity;\n\n // from gradient to solid\n if(new_mode == 'solid') {\n color = active_solid;\n if(options.transparency) {\n opacity = active_opacity;\n }\n }\n else {\n color = gradient_data.steps[0].color;\n if(options.transparency) {\n opacity = gradient_data.steps[0].opacity;\n }\n }\n\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = color;\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = color;\n\n if(options.transparency) {\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = opacity;\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = opacity;\n }\n\n // toggle grad fields\n if(options.modes.length >= 1) {\n document.querySelector('.pccp_deg_f_wrap').style.display = (new_mode == 'linear-gradient') ? 'flex' : 'none';\n document.querySelector('.pccp_circle_f_wrap').style.display = (new_mode == 'radial-gradient') ? 'block' : 'none';\n }\n\n // toogle gradient wizard\n if(options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) {\n document.querySelector('.lccp_gradient_wizard').style.display = (new_mode != 'solid') ? 'block' : 'none';\n }\n\n document.querySelectorAll('#lccp_modes_wrap span').forEach(m => m.classList.remove('lccp_sel_mode'));\n el.classList.add('lccp_sel_mode');\n\n active_mode = new_mode;\n (new_mode == 'solid') ? this.apply_changes() : this.apply_gradient_changes(true);\n };\n\n\n // add gradient step\n this.add_gradient_step = function(e) {\n const $this = this,\n pos = Math.round((100 * e.layerX) / e.target.offsetWidth);\n\n // inject in actual steps\n let index = 0;\n for(let step of gradient_data.steps) {\n\n if(step.position > pos) {\n const step_data = {\n color : (index - 1 < 0) ? step.color : gradient_data.steps[(index - 1)].color,\n opacity : 1,\n position : pos\n }\n\n gradient_data.steps.splice(index, 0, step_data);\n break;\n }\n\n index++;\n }\n document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove());\n\n gradient_data.steps.some(function(step, index) {\n $this.add_draggable_element(index, step.position, step.color);\n });\n\n // select newly added element\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ index +'\"]').click();\n\n this.apply_gradient_changes(true);\n };\n\n\n // apply ellipse or circle\n this.set_ellipse_circle = function(el, new_opt) {\n if(gradient_data.radial_circle && new_opt == 'circle' || !gradient_data.radial_circle && new_opt != 'circle') {\n return false;\n }\n gradient_data.radial_circle = !gradient_data.radial_circle;\n\n document.querySelectorAll('.pccp_circle_f_wrap span').forEach(m => m.classList.remove('pcpp_circle_btn_active'));\n el.classList.add('pcpp_circle_btn_active');\n\n this.apply_gradient_changes(true);\n };\n\n\n // track opacity range fields change\n this.track_deg_range_change = function(e) {\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').value = e.target.value;\n\n gradient_data.deg = e.target.value;\n this.apply_gradient_changes(true);\n };\n this.track_deg_num_change = function(e) {\n let val = parseFloat(e.target.value);\n if(isNaN(val) || val < 0 || val > 360) {\n val = 90;\n }\n\n e.target.value = val;\n if(document.querySelector('.pccp_deg_f_wrap input[type=range]')) {\n document.querySelector('.pccp_deg_f_wrap input[type=range]').value = val;\n }\n\n gradient_data.deg = val;\n this.apply_gradient_changes(true);\n };\n\n\n // track opacity range fields change\n this.track_color_change = function(e) {\n const val = e.target.value.toLowerCase();\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = val;\n\n this.apply_color_change(val);\n };\n this.track_color_hex_change = function(e) {\n let val = this.short_hex_fix(e.target.value);\n\n if(val.match(/^#[a-f0-9]{6}$/i) === null) {\n val = active_solid.toLowerCase();\n }\n\n e.target.value = val;\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = val;\n\n this.apply_color_change(val);\n };\n this.apply_color_change = function(val) {\n if(active_mode == 'solid') {\n active_solid = val;\n this.apply_changes();\n }\n else {\n gradient_data.steps[ sel_grad_step ].color = val;\n\n document.querySelector('.lccp_sel_step').style.background = val;\n this.apply_gradient_changes(true);\n }\n };\n\n\n // track opacity range fields change\n this.track_opacity_range_change = function(e) {\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = e.target.value;\n this.alter_hex_opacity(e.target.value);\n };\n this.track_opacity_num_change = function(e) {\n let val = parseFloat(e.target.value);\n if(isNaN(val) || val < 0 || val > 1) {\n val = 0.5;\n }\n\n e.target.value = val;\n\n if(document.querySelector('.pccp_opacity_f_wrap input[type=range]')) {\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = val;\n this.alter_hex_opacity(val);\n }\n };\n this.alter_hex_opacity = function(opacity) {\n document.querySelector('#lc-color-picker input[type=\"color\"]').style.opacity = opacity;\n\n if(active_mode == 'solid') {\n active_opacity = opacity;\n this.apply_changes();\n }\n else {\n gradient_data.steps[ sel_grad_step ].opacity = opacity;\n this.apply_gradient_changes(true);\n }\n };\n\n\n\n\n\n /*\n * UTILITY FUNCTION - debounce action to run once after X time\n *\n * @param (string) action_name\n * @param (int) timing - milliseconds to debounce\n * @param (string) - class method name to call after debouncing\n * @param (mixed) - extra parameters to pass to callback function\n */\n this.debounce = function(action_name, timing, cb_function, cb_params) {\n if( typeof(debounced_vars[ action_name ]) != 'undefined' && debounced_vars[ action_name ]) {\n clearTimeout(debounced_vars[ action_name ]);\n }\n const $this = this;\n\n debounced_vars[ action_name ] = setTimeout(() => {\n $this[cb_function].call($this, cb_params);\n }, timing);\n };\n\n\n\n\n\n /* CSS - creates inline CSS into the page */\n this.generate_style = function() {\n const transp_bg_img = \"url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkICgoMDAwMDAoMDA0NDAwRERERERQUFBQUFBQUFBT/2wBDAQQFBQgHCA8KCg8UDg4OFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAoACgDAREAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAUGCP/EACIQAQAABQMFAQAAAAAAAAAAAAAFFUNjgqLB4RESEzVRkf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDv4AE6K0stgTgAaIAE6K0stgTgAUZra1cATW1q4A9na8WXXu/PgEqu6eQJVd08gnAAowqrjuCiADOgAowqrjuCiAD/2Q==')\";\n\n document.head.insertAdjacentHTML('beforeend',\n``);\n };\n\n\n // init when called\n this.init();\n };\n\n\n\n\n\n\n // UTILITIES\n\n // sanitize \"selector\" parameter allowing both strings and DOM objects\n const maybe_querySelectorAll = (selector) => {\n\n if(typeof(selector) != 'string') {\n if(selector instanceof Element) { // JS or jQuery\n return [selector];\n }\n else {\n let to_return = [];\n\n for(const obj of selector) {\n if(obj instanceof Element) {\n to_return.push(obj);\n }\n }\n return to_return;\n }\n }\n\n // clean problematic selectors\n (selector.match(/(#[0-9][^\\s:,]*)/g) || []).forEach(function(n) {\n selector = selector.replace(n, '[id=\"' + n.replace(\"#\", \"\") + '\"]');\n });\n\n return document.querySelectorAll(selector);\n };\n\n\n})());\n"],"names":["global","factory","this","window","lc_color_picker","debounced_vars","window_width","style_generated","active_trigger","active_trig_id","active_solid","active_opacity","active_gradient","active_mode","sel_grad_step","gradient_data","deg","radial_circle","steps","def_opts","modes","open_on_focus","transparency","dark_theme","no_input_mode","wrap_width","preview_style","input_padding","side","width","separator_color","fallback_colors","on_change","labels","right_input_selector","lccp_ivc_event","picker_id","hide_picker","CustomEvent","bubbles","detail","document","addEventListener","e","picker","querySelector","target","classList","contains","trigger","getElementsByClassName","parentNode","getElementById","getAttribute","dispatchEvent","innerWidth","String","prototype","lccpReplaceArray","find","replace","replaceString","i","length","regex","RegExp","attachTo","cp_uniqid","last_tracked_col","options","console","error","bkp_opts","Object","assign","init","$this","generate_style","maybe_querySelectorAll","forEach","el","tagName","wrap_element","Math","random","toString","substr","side_prop","trigger_css","parseInt","getComputedStyle","trigger_upper_css","value","div","createElement","className","setAttribute","style","round","getBoundingClientRect","direct_colorpicker_code","add","innerHTML","insertBefore","appendChild","paddingRight","paddingLeft","background","show_picker","remove","debounce","keyCode","key","is_active_trigger_and_opened","activeElement","curr_val","test","val_to_set","browser_val","trim","replaceAll","rgb","RGB_to_hex","toLowerCase","indexOf","call","$target","direct_colorpicker","navigator","userAgent","includes","click","val_to_picker","append_color_picker","picker_w","offsetWidth","picker_h","offsetHeight","at_offsety","at_h","clientHeight","y_pos","y","pageYOffset","left","right","floor","y_pos_css","documentElement","scrollTop","innerHeight","from_manual_input","val","load_gradient_data","load_solid_data","raw_data","data","RGBA_to_hexA","short_hex_fix","is_radial","raw_steps","split","fallback_multiplier","some","raw_step","index","position","raw_color","opacity","col_arr","push","color","rgba_arr","alpha","substring","splice","join","parseFloat","rgb_arr","r","g","b","shorten_hex","hex","a","hex_to_RGB","h","hex_to_RGBA","on_manual_input_change","theme_class","shown_solid","shown_opacity","print_grad_code","picker_el","body","insertAdjacentHTML","mode","querySelectorAll","mode_change","step","add_draggable_element","add_gradient_step","track_deg_range_change","track_deg_num_change","set_ellipse_circle","track_color_change","track_opacity_range_change","track_opacity_num_change","rel_step_num","container","sel_class","del_btn_vis","active","dragStart","range_id","dragEnd","apply_changes","drag","range","preventDefault","rect","new_pos","type","touches","clientX","min_pos","max_pos","apply_gradient_changes","step_num","removeEventListener","select_gradient_color","btn","setTimeout","parent","to_select","m","also_apply_changes","new_gradient","colors_part","to_add","field","undefined","clearTimeout","new_mode","display","pos","layerX","step_data","new_opt","isNaN","apply_color_change","track_color_hex_change","match","alter_hex_opacity","action_name","timing","cb_function","cb_params","head","selector","Element","to_return","obj","n","exports","module","define","amd","globalThis","self","Chart"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/lc_color_picker.min.map b/amd/build/lc_color_picker.min.map new file mode 100644 index 0000000..0240471 --- /dev/null +++ b/amd/build/lc_color_picker.min.map @@ -0,0 +1 @@ +{"version":3,"file":"lc_color_picker.min.js","sources":["../src/lc_color_picker.js"],"sourcesContent":["/**\r\n * lc_color_picker.js - The colorpicker for modern web\r\n * Version: 2.0.0\r\n * Author: Luca Montanari (LCweb)\r\n * Website: https://lcweb.it\r\n * Licensed under the MIT license\r\n */\r\n\r\n\r\n(function (global, factory) {\r\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\r\n typeof define === 'function' && define.amd ? define(factory) :\r\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory());\r\n })(this, (function () { 'use strict';\r\n\r\n if(typeof(window.lc_color_picker) != 'undefined') {return false;} // prevent multiple script inits\r\n\r\n\r\n /*** vars ***/\r\n let debounced_vars = [],\r\n window_width = null,\r\n\r\n style_generated = null,\r\n active_trigger = null,\r\n active_trig_id = null,\r\n\r\n active_solid = null,\r\n active_opacity = null,\r\n active_gradient = null,\r\n active_mode = 'linear-gradient',\r\n\r\n sel_grad_step = 0, // selected gradient step\r\n gradient_data = {\r\n deg: 0,\r\n radial_circle: false,\r\n steps: [\r\n //{color : null, opacity: null, position : null}\r\n ],\r\n };\r\n\r\n\r\n /*** default options ***/\r\n const def_opts = {\r\n modes : ['linear-gradient'], // (array) containing supported modes (solid | linear-gradient | radial-gradient)\r\n open_on_focus : true, // (bool) whether to open the picker when field is focused\r\n transparency : true, // (bool) whether to allow colors transparency tune\r\n dark_theme : false, // (bool) whether to enable dark picker theme\r\n no_input_mode : false, // (bool) whether to stretch the trigger in order to cover the whole input field\r\n wrap_width : 'auto', // (string) defines the wrapper width. \"auto\" to leave it up to CSS, \"inherit\" to statically copy input field width, or any other CSS sizing\r\n preview_style : { // (object) defining shape and position of the in-field preview\r\n input_padding : 35, // extra px padding eventually added to the target input to not cover text\r\n side : 'right', // right or left\r\n width : 30,\r\n separator_color : '#ccc', // (string) CSS color applird to preview element as separator\r\n },\r\n fallback_colors : ['#008080', 'linear-gradient(90deg, #fff 0%, #000 100%)'], // (array) defining default colors used when trigger field has no value. First parameter for solid color, second for gradient\r\n\r\n on_change : null, // function(new_value, target_field) {}, - triggered every time field value changes. Passes value and target field object as parameters\r\n\r\n labels : [ // (array) option used to translate script texts\r\n 'click to change color',\r\n 'Solid',\r\n 'Linear Gradient',\r\n 'Radial Gradient',\r\n 'add gradient step',\r\n 'gradient angle',\r\n 'gradient shape',\r\n 'color',\r\n 'opacity',\r\n ],\r\n };\r\n\r\n\r\n // shortcut var to target the text input only\r\n const right_input_selector = 'input:not([type=\"color\"])';\r\n\r\n\r\n\r\n // input value check custom event\r\n const lccp_ivc_event = function(picker_id, hide_picker = false) {\r\n return new CustomEvent('lccp_input_val_check', {\r\n bubbles : true,\r\n detail: {\r\n picker_id : picker_id,\r\n hide_picker : hide_picker\r\n }\r\n });\r\n };\r\n\r\n\r\n\r\n /*** hide picker cicking outside ***/\r\n document.addEventListener('click', function(e) {\r\n const picker = document.querySelector(\"#lc-color-picker.lccp-shown\");\r\n if(!picker || e.target.classList.contains('lccp-preview')) {\r\n return true;\r\n }\r\n\r\n // is an element within a trigger?\r\n for (const trigger of document.getElementsByClassName('lccp-preview')) {\r\n if(trigger.contains(e.target)) {\r\n return true;\r\n }\r\n }\r\n\r\n // clicked on the same colorpicker field? keep visible\r\n if(e.target.parentNode && e.target.parentNode.classList && e.target.parentNode.classList.contains('lccp-el-wrap') && document.getElementById(active_trig_id)) {\r\n return true;\r\n }\r\n\r\n // close if clicked element is not in the picker\r\n if(!picker.contains(e.target) && !e.target.classList.contains('lccp-shown')) {\r\n const picker_id = picker.getAttribute('data-trigger-id'),\r\n $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector);\r\n\r\n $input.dispatchEvent(lccp_ivc_event(picker_id, true));\r\n }\r\n return true;\r\n });\r\n\r\n\r\n /* hide picker on screen resizing */\r\n window.addEventListener('resize', function(e) {\r\n const picker = document.querySelector(\"#lc-color-picker.lccp-shown\");\r\n if(!picker || window_width == window.innerWidth) {\r\n return true;\r\n }\r\n\r\n // check field value\r\n const picker_id = picker.getAttribute('data-trigger-id'),\r\n $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector);\r\n\r\n $input.dispatchEvent(lccp_ivc_event(picker_id, true));\r\n });\r\n\r\n\r\n /* extend string object to ReplaceArray */\r\n String.prototype.lccpReplaceArray = function(find, replace) {\r\n let replaceString = this;\r\n let regex;\r\n\r\n for (var i = 0; i < find.length; i++) {\r\n const regex = new RegExp(find[i], \"g\");\r\n replaceString = (typeof(replace) == 'object') ? replaceString.replace(regex, replace[i]) : replaceString.replace(regex, replace);\r\n }\r\n return replaceString;\r\n };\r\n\r\n\r\n\r\n\r\n /*** plugin class ***/\r\n window.lc_color_picker = function(attachTo, options = {}) {\r\n let cp_uniqid, // unique ID assigned to this colorpicker instance\r\n last_tracked_col;\r\n\r\n this.attachTo = attachTo;\r\n if(!this.attachTo) {\r\n return console.error('You must provide a valid selector string first argument');\r\n }\r\n\r\n // override options\r\n if(typeof(options) != 'object') {\r\n return console.error('Options must be an object');\r\n }\r\n\r\n const bkp_opts = options;\r\n options = Object.assign({}, def_opts, options);\r\n\r\n if(typeof(bkp_opts.preview_style) != 'undefined') {\r\n options.preview_style = Object.assign({}, def_opts.preview_style, bkp_opts.preview_style);\r\n }\r\n\r\n\r\n\r\n /* initialize */\r\n this.init = function() {\r\n const $this = this;\r\n\r\n // Generate style\r\n if(!style_generated) {\r\n this.generate_style();\r\n style_generated = true;\r\n }\r\n\r\n\r\n // assign to each target element\r\n maybe_querySelectorAll(attachTo).forEach(function(el) {\r\n if(el.tagName == 'INPUT' && el.getAttribute('type') != 'text') {\r\n return;\r\n }\r\n\r\n // do not initialize twice\r\n if(el.parentNode.classList.length && el.parentNode.classList.contains('lcslt_wrap')) {\r\n return;\r\n }\r\n\r\n $this.wrap_element(el);\r\n });\r\n };\r\n\r\n\r\n\r\n /* wrap target element to allow trigger display */\r\n this.wrap_element = function(el) {\r\n cp_uniqid = Math.random().toString(36).substr(2, 9);\r\n\r\n const $this = this,\r\n side_prop = (options.preview_style.side == 'right') ? 'borderRightWidth' : 'borderLeftWidth';\r\n\r\n let trigger_css =\r\n `width:${ (options.no_input_mode) ? 'calc(100% - '+ parseInt(getComputedStyle(el)['borderRightWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderLeftWidth'], 10) +'px);' : options.preview_style.width +'px;'}` +\r\n\r\n options.preview_style.side +':'+ parseInt(getComputedStyle(el)[side_prop], 10) +'px;'+\r\n\r\n 'top:'+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px;' +\r\n\r\n 'height: calc(100% - '+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderBottomWidth'], 10) +'px);';\r\n\r\n let trigger_upper_css =\r\n trigger_css +\r\n 'background:'+ el.value +';' +\r\n 'border-color:'+ options.preview_style.separator_color +';'\r\n\r\n let div = document.createElement('div');\r\n div.className = 'lccp-preview-'+ options.preview_style.side;\r\n div.setAttribute('data-for', el.getAttribute('name'));\r\n\r\n // static width from input?\r\n if(options.wrap_width != 'auto') {\r\n div.style.width = (options.wrap_width == 'inherit') ? Math.round(el.getBoundingClientRect().width) + 'px' : options.wrap_width;\r\n }\r\n\r\n const direct_colorpicker_code = (!options.transparency && options.modes.length == 1 && options.modes[0] == 'linear-gradient') ?\r\n '' : '';\r\n\r\n div.classList.add(\"lccp-el-wrap\");\r\n div.innerHTML =\r\n '' +\r\n '' +\r\n direct_colorpicker_code;\r\n\r\n el.parentNode.insertBefore(div, el);\r\n div.appendChild(el);\r\n\r\n // input padding\r\n if(!options.no_input_mode) {\r\n if(options.preview_style.side == 'right') {\r\n div.querySelector('input:not([type=\"color\"])').style.paddingRight = options.preview_style.input_padding +'px';\r\n } else {\r\n div.querySelector('input:not([type=\"color\"])').style.paddingLeft = options.preview_style.input_padding +'px';\r\n }\r\n }\r\n\r\n\r\n // direct browser colorpicker? track changes\r\n if(div.querySelector('.lccp-direct-cp-f')) {\r\n div.querySelector('.lccp-direct-cp-f').addEventListener(\"input\", (e) => {\r\n\r\n div.querySelector('input:not([type=\"color\"])').value = e.target.value;\r\n div.querySelector('.lccp-preview').style.background = e.target.value;\r\n });\r\n }\r\n\r\n\r\n // event to show picker\r\n const trigger = document.getElementById(cp_uniqid);\r\n trigger.addEventListener(\"click\", (e) => {\r\n this.show_picker(trigger);\r\n });\r\n\r\n\r\n\r\n // show on field focus?\r\n if(options.open_on_focus) {\r\n div.querySelector(right_input_selector).addEventListener(\"focus\", (e) => {\r\n if(trigger != active_trigger) {\r\n if(active_trigger) {\r\n document.getElementById('lc-color-picker').classList.remove('lccp-shown');\r\n active_trigger = null;\r\n }\r\n\r\n $this.debounce('open_on_focus', 10, 'show_picker', trigger);\r\n }\r\n });\r\n }\r\n\r\n\r\n // sync manually-inputed data in the field\r\n div.querySelector(right_input_selector).addEventListener(\"keyup\", (e) => {\r\n if(e.keyCode == 9 || e.key === 'Enter' || e.keyCode === 13) {\r\n return;\r\n }\r\n\r\n const is_active_trigger_and_opened = (active_trig_id = cp_uniqid && document.querySelector(\"#lc-color-picker.lccp-shown\")) ? true : false;\r\n\r\n active_trigger = trigger;\r\n active_trig_id = cp_uniqid;\r\n\r\n $this.debounce('manual_input_sync', 10, 'val_to_picker', true);\r\n\r\n if(is_active_trigger_and_opened) {\r\n $this.debounce('manual_input_sync_cp', 10, 'append_color_picker', false);\r\n $this.debounce('reopen_picker_after_manual_edit', 10, 'show_picker', trigger);\r\n }\r\n });\r\n\r\n\r\n // be sure input value is managed on focusout\r\n div.querySelector(right_input_selector).addEventListener(\"focusout\", (e) => {\r\n // not if this field's picker is shown and focus is on \"body\"\r\n if(document.activeElement.tagName == 'BODY' && document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ active_trig_id +'\"]')) {\r\n return true;\r\n }\r\n\r\n e.target.dispatchEvent(lccp_ivc_event(active_trig_id, true));\r\n });\r\n\r\n\r\n // custom event - check field validity and eventually use fallback values\r\n div.querySelector(right_input_selector).addEventListener(\"lccp_input_val_check\", (e) => {\r\n const curr_val = e.target.value,\r\n test = document.createElement('div');\r\n\r\n test.style.background = curr_val;\r\n let browser_val = test.style.background,\r\n val_to_set;\r\n\r\n if(!curr_val.trim().length || !browser_val) {\r\n if(e.target.value.toLowerCase().indexOf('gradient') === -1) {\r\n val_to_set = (options.fallback_colors[0].toLowerCase().indexOf('rgba') === -1) ? $this.RGB_to_hex(options.fallback_colors[0]) : options.fallback_colors[0];\r\n }\r\n else {\r\n val_to_set = options.fallback_colors[1];\r\n }\r\n }\r\n else {\r\n // browser already fixes minor things\r\n browser_val = browser_val.replaceAll('0.', '.').replace(/rgb\\([^\\)]+\\)/g, (rgb) => {\r\n return $this.RGB_to_hex(rgb);\r\n });\r\n\r\n val_to_set = (browser_val.trim().toLowerCase().substr(0, 4) == 'rgb(') ? $this.RGB_to_hex(browser_val) : browser_val;\r\n }\r\n\r\n if(val_to_set != curr_val) {\r\n e.target.value = val_to_set;\r\n }\r\n\r\n if(typeof(options.on_change) == 'function' && last_tracked_col != val_to_set) {\r\n options.on_change.call($this, val_to_set, e.target);\r\n }\r\n\r\n if(e.detail.picker_id == active_trig_id) {\r\n active_trigger = null;\r\n active_trig_id = null;\r\n }\r\n\r\n\r\n // also hide picker?\r\n const $target = document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ e.detail.picker_id +'\"]');\r\n if($target) {\r\n\r\n $target.classList.remove('lccp-shown');\r\n document.getElementById(\"lc-color-picker\").remove();\r\n }\r\n });\r\n };\r\n\r\n\r\n\r\n /* show picker */\r\n this.show_picker = function(trigger) {\r\n if(document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id=\"'+ active_trig_id +'\"]')) {\r\n document.getElementById(\"lc-color-picker\").remove();\r\n active_trigger = null;\r\n active_trig_id = null\r\n\r\n return false;\r\n }\r\n\r\n // direct colorpicker usage? Not for Firefox is \"show on focus\" is enabled\r\n const direct_colorpicker = trigger.parentNode.querySelector('.lccp-direct-cp-f');\r\n if(\r\n direct_colorpicker &&\r\n (\r\n !options.open_on_focus ||\r\n (options.open_on_focus && !navigator.userAgent.toLowerCase().includes('firefox'))\r\n )\r\n ) {\r\n direct_colorpicker.value = active_solid;\r\n direct_colorpicker.click();\r\n return true;\r\n }\r\n\r\n\r\n window_width = window.innerWidth;\r\n active_trigger = trigger;\r\n active_trig_id = cp_uniqid;\r\n\r\n this.val_to_picker();\r\n this.append_color_picker();\r\n\r\n const picker = document.getElementById('lc-color-picker'),\r\n picker_w = picker.offsetWidth,\r\n picker_h = picker.offsetHeight,\r\n at_offsety = active_trigger.getBoundingClientRect(),\r\n at_h = parseInt(active_trigger.clientHeight, 10) + parseInt(getComputedStyle(active_trigger)['borderTopWidth'], 10) + parseInt(getComputedStyle(active_trigger)['borderBottomWidth'], 10),\r\n y_pos = (parseInt(at_offsety.y, 10) + parseInt(window.pageYOffset, 10) + at_h + 5);\r\n\r\n // left pos control - also checking side overflows\r\n let left = (parseInt(at_offsety.right, 10) - picker_w);\r\n if(left < 0) {\r\n left = 0;\r\n }\r\n\r\n // mobile? show it centered\r\n if(window.innerWidth < 700) {\r\n left = Math.floor( (window.innerWidth - picker_w) / 2);\r\n }\r\n\r\n // top or bottom ?\r\n const y_pos_css = (y_pos + picker_h - document.documentElement.scrollTop < window.innerHeight) ?\r\n 'top:'+ y_pos :\r\n 'transform: translate3d(0, calc((100% + '+ (active_trigger.offsetHeight + 10) +'px) * -1), 0); top:'+ y_pos;\r\n\r\n picker.setAttribute('style', y_pos_css +'px; left: '+ left +'px;');\r\n picker.classList.add('lccp-shown');\r\n };\r\n\r\n\r\n\r\n /* handles input value and prepres data for the picker */\r\n this.val_to_picker = function(from_manual_input) {\r\n if(!active_trigger) {\r\n return false;\r\n }\r\n const val = active_trigger.parentNode.querySelector(right_input_selector).value.trim().toLowerCase();\r\n last_tracked_col = val;\r\n\r\n // check validity\r\n let test = document.createElement('div');\r\n test.style.background = val;\r\n\r\n //// set active colors\r\n // if no value found\r\n if(!val.length || !test.style.background.length) {\r\n active_solid = options.fallback_colors[0];\r\n active_gradient = options.fallback_colors[1];\r\n active_mode = 'linear-gradient';\r\n\r\n /* if(val.indexOf('linear-gradient') !== -1) {\r\n }\r\n else if(val.indexOf('radial-gradient') !== -1) {\r\n active_mode = 'radial-gradient';\r\n }\r\n else {\r\n active_mode = 'solid';\r\n } */\r\n }\r\n else {\r\n\r\n active_mode = 'linear-gradient';\r\n active_gradient = val;\r\n // find which value type has been passed\r\n /* if(val.indexOf('linear-gradient') !== -1) {\r\n }\r\n else if(val.indexOf('radial-gradient') !== -1) {\r\n active_mode = 'radial-gradient';\r\n }\r\n else {\r\n active_mode = 'solid';\r\n }\r\n\r\n if(active_mode == 'solid') {\r\n active_solid = val;\r\n active_gradient = options.fallback_colors[1];\r\n }\r\n else{\r\n active_solid = options.fallback_colors[0];\r\n } */\r\n }\r\n active_trigger.style.background = val;\r\n\r\n if(!from_manual_input || (from_manual_input && options.open_on_focus)) {\r\n // elaborate solid color data (color and alpha)\r\n //this.load_solid_data(active_solid);\r\n // elaborate gradient data\r\n if(active_gradient) {\r\n this.load_gradient_data(active_gradient);\r\n }\r\n }\r\n };\r\n\r\n\r\n\r\n /* elaborate solid color data (color and alpha) loading into active_solid and active_opacity */\r\n this.load_solid_data = function(raw_data) {\r\n active_opacity = 1;\r\n\r\n // rgba\r\n if(raw_data.indexOf('rgba') !== -1) {\r\n const data = this.RGBA_to_hexA(raw_data);\r\n active_solid = data[0];\r\n active_opacity = data[1];\r\n }\r\n\r\n // rgb\r\n else if(raw_data.indexOf('rgba') !== -1) {\r\n active_solid = this.RGB_to_hex(raw_data);\r\n }\r\n\r\n // hex\r\n else {\r\n active_solid = this.short_hex_fix(raw_data);\r\n }\r\n };\r\n\r\n\r\n\r\n /* elaborate gradient data loading into gradient_data */\r\n this.load_gradient_data = function(raw_data) {\r\n const $this = this;\r\n const is_radial = (raw_data.indexOf('radial-gradient') === -1) ? false : true;\r\n\r\n // solve issues with inner RGB|RGBA and turn everything into RGBA\r\n raw_data = raw_data\r\n .replace(/,\\./g, ',0.').replace(/ \\./g, ' 0.')\r\n .replace(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+(?:\\.\\d+)?))?\\)/g, 'rgbaZ($1|$2|$3|$4)')\r\n .replace(/\\|\\)/g, '|1)');\r\n\r\n // names to deg\r\n raw_data = raw_data\r\n .replace('top right', '45deg').replace('right top', '45deg')\r\n .replace('bottom right', '135deg').replace('bottom right', '135deg')\r\n .replace('top left', '315deg').replace('left top', '315deg')\r\n .replace('bottom left', '225deg').replace('bottom left', '225deg')\r\n .replace('right', '90deg').replace('left', '270deg').replace('top', '0deg').replace('bottom', '180deg');\r\n\r\n // be sure deg or shape is defined\r\n if(is_radial && raw_data.indexOf('ellipse') === -1 && raw_data.indexOf('circle') === -1) {\r\n raw_data.replace('\\\\(', '(ellipse ');\r\n }\r\n if(!is_radial && raw_data.indexOf('deg') === -1) {\r\n raw_data.replace('\\\\(', '(180deg');\r\n }\r\n\r\n // process\r\n raw_data = raw_data.lccpReplaceArray(\r\n ['linear-gradient', 'radial-gradient', '', '\\\\(', 'to', '\\\\)'],\r\n ''\r\n );\r\n\r\n // split steps\r\n const raw_steps = raw_data.split(',');\r\n const fallback_multiplier = 100 / raw_steps.length;\r\n\r\n gradient_data.steps = [];\r\n raw_steps.some(function(raw_step, index) {\r\n\r\n // direction on first index\r\n if(!index) {\r\n if(is_radial) {\r\n gradient_data.radial_circle = (raw_step.indexOf('circle') === -1) ? false : true;\r\n } else {\r\n gradient_data.deg = parseInt(raw_step.replace('deg', ''), 10);\r\n }\r\n }\r\n\r\n // {color : null, opacity: null, position : null}\r\n else {\r\n raw_step = raw_step.trim().split(' ');\r\n let position = '';\r\n\r\n // position\r\n if(raw_step.length < 2) {\r\n if(index === 1) {\r\n position = '0%';\r\n }\r\n else if(index == (raw_steps.length - 1)) {\r\n position = '100%';\r\n }\r\n else {\r\n position = (fallback_multiplier * index) +'%';\r\n }\r\n }\r\n else {\r\n position = raw_step[1];\r\n }\r\n\r\n // color\r\n let raw_color = raw_step[0],\r\n opacity = 1;\r\n\r\n // normalize to hex\r\n if(raw_color.indexOf('rgbaZ') !== -1) {\r\n const col_arr = $this.RGBA_to_hexA(\r\n raw_color.replace('rgbaZ', 'rgba').replace(/\\|/g, ',')\r\n );\r\n\r\n raw_color = col_arr[0];\r\n opacity = col_arr[1];\r\n }\r\n\r\n gradient_data.steps.push({\r\n color : $this.short_hex_fix(raw_color),\r\n opacity: opacity,\r\n position : parseInt(position, 10)\r\n });\r\n }\r\n });\r\n };\r\n\r\n\r\n\r\n /* handles RGBA string returning a two elements array: hex and alpha */\r\n this.RGBA_to_hexA = function(raw_data) {\r\n raw_data = raw_data.lccpReplaceArray(['rgba', '\\\\(', '\\\\)'], '');\r\n const rgba_arr = raw_data.split(',')\r\n\r\n let alpha = (typeof(rgba_arr[3]) != 'undefined') ? rgba_arr[3] : '1';\r\n if(alpha.substring(0, 1) == '.') {\r\n alpha = 0 + alpha;\r\n }\r\n rgba_arr.splice(3, 1);\r\n\r\n return [\r\n this.RGB_to_hex('rgb('+ rgba_arr.join(',') +')'),\r\n parseFloat(alpha)\r\n ];\r\n };\r\n\r\n\r\n\r\n /* convert RGB to hex */\r\n this.RGB_to_hex = function(rgb) {\r\n rgb = rgb.lccpReplaceArray(['rgb', '\\\\(', '\\\\)'], '');\r\n const rgb_arr = rgb.split(',');\r\n\r\n if(rgb_arr.length < 3) {\r\n return '';\r\n }\r\n\r\n let r = parseInt(rgb_arr[0].trim(), 10).toString(16),\r\n g = parseInt(rgb_arr[1].trim(), 10).toString(16),\r\n b = parseInt(rgb_arr[2].trim(), 10).toString(16);\r\n\r\n if (r.length == 1) {r = \"0\" + r;}\r\n if (g.length == 1) {g = \"0\" + g;}\r\n if (b.length == 1) {b = \"0\" + b;}\r\n\r\n return this.shorten_hex(r + g + b);\r\n };\r\n\r\n\r\n\r\n /* if possible, shortenize hex string */\r\n this.shorten_hex = function(hex) {\r\n hex = hex.replace('#', '').split('');\r\n\r\n if(hex.length >= 6) {\r\n if(\r\n hex[0] === hex[1] &&\r\n hex[2] === hex[3] &&\r\n hex[4] === hex[5]\r\n ) {\r\n return '#'+ hex[0] + hex[2] + hex[4];\r\n }\r\n }\r\n\r\n return '#'+ hex.join('');\r\n };\r\n\r\n\r\n\r\n /* convert short hex to full format */\r\n this.short_hex_fix = function(hex) {\r\n if(hex.length == 4) {\r\n const a = hex.split('');\r\n hex = a[0] + a[1] + a[1] + a[2] + a[2] + a[3] + a[3];\r\n }\r\n\r\n return hex.toLowerCase();\r\n };\r\n\r\n\r\n\r\n /* convert hex to RGB */\r\n this.hex_to_RGB = function(h) {\r\n let r = 0, g = 0, b = 0;\r\n\r\n // 3 digits\r\n if (h.length == 4) {\r\n r = \"0x\" + h[1] + h[1];\r\n g = \"0x\" + h[2] + h[2];\r\n b = \"0x\" + h[3] + h[3];\r\n\r\n // 6 digits\r\n } else if (h.length == 7) {\r\n r = \"0x\" + h[1] + h[2];\r\n g = \"0x\" + h[3] + h[4];\r\n b = \"0x\" + h[5] + h[6];\r\n }\r\n\r\n return \"rgb(\"+ +r + \", \" + +g + \", \" + +b + \")\";\r\n };\r\n\r\n\r\n\r\n /* convert hex to RGB */\r\n this.hex_to_RGBA = function(h, opacity) {\r\n if(parseFloat(opacity) === 1) {\r\n return this.shorten_hex(h);\r\n }\r\n\r\n let rgb = this.hex_to_RGB(h);\r\n return rgb.replace('(', 'a(').replace(')', ', '+ opacity.toString().replace('0.', '.') +')');\r\n };\r\n\r\n\r\n\r\n\r\n /* append color container picker to the body */\r\n this.append_color_picker = function(on_manual_input_change = false) {\r\n const $this = this;\r\n\r\n /* if(document.getElementById(\"lc-color-picker\") && !on_manual_input_change) {\r\n document.getElementById(\"lc-color-picker\").remove();\r\n } */\r\n\r\n const theme_class = (options.dark_theme) ? 'lccp_dark_theme' : 'lccp_light_theme',\r\n bg = (active_mode == 'solid') ? active_solid : active_gradient,\r\n shown_solid = (active_mode == 'solid') ? active_solid : gradient_data.steps[0].color,\r\n shown_opacity = (active_mode == 'solid') ? active_opacity : (options.transparency) ? gradient_data.steps[0].opacity : null,\r\n print_grad_code = (options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) ? true : false;\r\n\r\n\r\n // start code\r\n let picker = '',\r\n picker_el;\r\n\r\n if(on_manual_input_change && document.getElementById(\"lc-color-picker\")) {\r\n picker_el = document.getElementById(\"lc-color-picker\");\r\n picker_el.setAttribute('data-mode', active_mode);\r\n picker_el.setAttribute('data-trigger-id', cp_uniqid);\r\n }\r\n else {\r\n picker = '
';\r\n }\r\n\r\n\r\n // modes select\r\n /* if(options.modes.length >= 1) {\r\n picker += `\r\n
\r\n ${ options.labels[1] }\r\n ${ options.labels[2] }\r\n ${ options.labels[3] }\r\n
`;\r\n } */\r\n\r\n\r\n // gradient wizard\r\n if(print_grad_code) {\r\n picker += `\r\n
\r\n
\r\n
\r\n\r\n
\r\n\r\n
\r\n \"angle\"\r\n\r\n \r\n \r\n
\r\n
\r\n \"shape\"\r\n\r\n Ellipse\r\n Circle\r\n
\r\n
\r\n
`;\r\n }\r\n\r\n\r\n // HTML5 colorpicker\r\n picker += `\r\n
\r\n \"color\"\r\n\r\n
\r\n \r\n
\r\n \r\n
`;\r\n\r\n // opacity cursor\r\n if(options.transparency) {\r\n picker += `\r\n
\r\n \"opacity\"\r\n\r\n \r\n \r\n
`;\r\n }\r\n\r\n\r\n // append or re-fill\r\n (on_manual_input_change && document.getElementById(\"lc-color-picker\")) ? picker_el.innerHTML = picker : document.body.insertAdjacentHTML('beforeend', picker +'
');\r\n\r\n\r\n // modes change\r\n if(options.modes.length >= 1) {\r\n for (const mode of document.querySelectorAll('#lccp_modes_wrap span')) {\r\n mode.addEventListener(\"click\", (e) => { $this.mode_change( e.target, e.target.getAttribute('data-mode')) });\r\n }\r\n }\r\n\r\n // print steps and add gradient step action\r\n if(print_grad_code) {\r\n gradient_data.steps.some(function(step, index) {\r\n $this.add_draggable_element(index, step.position, step.color);\r\n });\r\n\r\n document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').addEventListener(\"click\", (e) => {this.add_gradient_step(e) });\r\n }\r\n\r\n // angle actions\r\n if(options.modes.indexOf('linear-gradient') !== -1) {\r\n document.querySelector('.pccp_deg_f_wrap input[type=range]').addEventListener(\"input\", (e) => {this.track_deg_range_change(e)});\r\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener(\"change\", (e) => {this.track_deg_num_change(e)});\r\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener(\"keyup\", (e) => {\r\n this.debounce('deg_f_change', 500, 'track_deg_num_change', e);\r\n });\r\n }\r\n\r\n // circle actions\r\n if(options.modes.indexOf('radial-gradient') !== -1) {\r\n for (const mode of document.querySelectorAll('.pccp_circle_f_wrap span')) {\r\n mode.addEventListener(\"click\", (e) => { $this.set_ellipse_circle( e.target, e.target.getAttribute('data-val')) });\r\n }\r\n }\r\n\r\n // color actions\r\n document.querySelector('.pccp_color_f_wrap input[type=\"color\"]').addEventListener(\"input\", (e) => {this.track_color_change(e)});\r\n document.querySelector('.pccp_color_f_wrap input[type=\"color\"]').addEventListener(\"change\", (e) => {this.track_color_change(e)});\r\n document.querySelector('.pccp_color_f_wrap input[name=hex]').addEventListener(\"keyup\", (e) => {\r\n this.debounce('hex_f_change', 600, 'track_color_hex_change', e);\r\n });\r\n\r\n // transparency actions\r\n if(options.transparency) {\r\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').addEventListener(\"input\", (e) => {this.track_opacity_range_change(e)});\r\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener(\"change\", (e) => {this.track_opacity_num_change(e)});\r\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener(\"keyup\", (e) => {\r\n this.debounce('opacity_f_change', 500, 'track_opacity_num_change', e);\r\n });\r\n }\r\n };\r\n\r\n\r\n\r\n /*** add draggable element ***/\r\n this.add_draggable_element = function(rel_step_num, position, color) {\r\n const $this = this,\r\n container = document.querySelector('.lccp_gradient_ranges'),\r\n sel_class = (!rel_step_num) ? 'lccp_sel_step' : '',\r\n del_btn_vis = (gradient_data.steps.length > 2) ? '' : 'style=\"display: none;\"'\r\n\r\n container.innerHTML +=\r\n ''+\r\n ''+\r\n '';\r\n\r\n let active = false;\r\n\r\n //////\r\n const dragStart = function(range_id, el, e) {\r\n active = range_id;\r\n };\r\n\r\n const dragEnd = function() {\r\n active = false;\r\n $this.apply_changes();\r\n };\r\n\r\n const drag = function(range_id, range, e) {\r\n if (active !== false && range_id == active) {\r\n e.preventDefault();\r\n const rect = container.getBoundingClientRect();\r\n\r\n let new_pos = (e.type === \"touchmove\") ? (e.touches[0].clientX - rect.left) : (e.clientX - rect.left);\r\n new_pos = Math.round((100 * new_pos) / container.offsetWidth);\r\n\r\n if(new_pos < 0) {new_pos = 0;}\r\n else if(new_pos > 100) {new_pos = 100;}\r\n\r\n // limit positions basing on previous and next step\r\n const min_pos = (!range_id) ? 0 : gradient_data.steps[ range_id-1 ].position;\r\n const max_pos = (range_id == (gradient_data.steps.length - 1)) ? 100 : gradient_data.steps[ range_id+1 ].position;\r\n\r\n if(new_pos < min_pos) {new_pos = min_pos + 1;}\r\n else if(new_pos > max_pos) {new_pos = max_pos - 1;}\r\n\r\n gradient_data.steps[ range_id ].position = new_pos;\r\n range.style.left = new_pos +'%';\r\n\r\n $this.apply_gradient_changes();\r\n }\r\n };+\r\n /////\r\n\r\n document.querySelectorAll('.lccp_gradient_range').forEach(range => {\r\n const step_num = parseInt(range.getAttribute('data-step-num'), 10);\r\n\r\n range.removeEventListener(\"touchstart\", null);\r\n range.removeEventListener(\"touchend\", null);\r\n range.removeEventListener(\"touchmove\", null);\r\n range.removeEventListener(\"click\", null);\r\n\r\n range.removeEventListener(\"mousedown\", null);\r\n range.removeEventListener(\"mouseup\", null);\r\n\r\n range.addEventListener(\"touchstart\", (e) => {dragStart(step_num, e.target, e)});\r\n range.addEventListener(\"mousedown\", (e) => {dragStart(step_num, e.target, e)});\r\n\r\n range.addEventListener(\"click\", (e) => {$this.select_gradient_color(step_num)});\r\n\r\n container.addEventListener(\"touchmove\", (e) => {drag(step_num, range, e)});\r\n container.addEventListener(\"mousemove\", (e) => {drag(step_num, range, e)});\r\n\r\n range.addEventListener(\"mouseup\", (e) => {dragEnd()});\r\n range.addEventListener(\"touchend\", (e) => {dragEnd()});\r\n document.addEventListener(\"mouseup\", (e) => {dragEnd()});\r\n });\r\n\r\n\r\n // remove step handler\r\n document.querySelectorAll('.lccp_gradient_range img').forEach((btn) => {\r\n\r\n btn.addEventListener(\"click\", (e) => {\r\n if(document.querySelectorAll('.lccp_gradient_range').length < 3) {\r\n return false;\r\n }\r\n\r\n // wait a bit to not interfere with global handler for picker closing\r\n setTimeout(() => {\r\n const parent = e.target.parentNode,\r\n step_num = parseInt(parent.getAttribute('data-step-num'), 10),\r\n to_select = (!step_num) ? 0 : step_num - 1;\r\n\r\n gradient_data.steps.splice(step_num, 1);\r\n\r\n // clean and restart\r\n document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove());\r\n\r\n gradient_data.steps.some(function(step, index) {\r\n $this.add_draggable_element(index, step.position, step.color);\r\n });\r\n\r\n // select newly added element\r\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ to_select +'\"]').click();\r\n\r\n this.apply_gradient_changes(true);\r\n }, 20);\r\n });\r\n });\r\n };\r\n\r\n\r\n\r\n /* select gradient color */\r\n this.select_gradient_color = function(step_num) {\r\n sel_grad_step = step_num;\r\n\r\n document.querySelectorAll('.lccp_gradient_range').forEach(m => m.classList.remove('lccp_sel_step'));\r\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ step_num +'\"]').classList.add('lccp_sel_step');\r\n\r\n active_solid = gradient_data.steps[ step_num ].color;\r\n active_opacity = gradient_data.steps[ step_num ].opacity;\r\n\r\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = active_solid;\r\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = active_solid;\r\n\r\n if(options.transparency) {\r\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = active_opacity;\r\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = active_opacity;\r\n }\r\n };\r\n\r\n\r\n\r\n /* apply changes to gradient, after a color/opacity/degree update */\r\n this.apply_gradient_changes = function(also_apply_changes) {\r\n const $this = this;\r\n\r\n let new_gradient = active_mode+'(';\r\n new_gradient += (active_mode == 'linear-gradient') ? gradient_data.deg+'deg' : (gradient_data.radial_circle) ? 'circle' : 'ellipse';\r\n new_gradient += ', ';\r\n\r\n let colors_part = []\r\n gradient_data.steps.some(function(step, index) {\r\n\r\n let to_add = (options.transparency) ? $this.hex_to_RGBA(step.color, step.opacity) : $this.shorten_hex(step.color);\r\n\r\n if(\r\n gradient_data.steps.length > 2 ||\r\n (\r\n gradient_data.steps.length <= 2 &&\r\n (\r\n (!index && parseInt(step.position, 10)) ||\r\n (index && index < (gradient_data.steps.length - 1)) ||\r\n (index == (gradient_data.steps.length - 1) && parseInt(step.position, 10) != 100)\r\n )\r\n )\r\n ) {\r\n to_add += ' '+ step.position +'%';\r\n }\r\n\r\n colors_part.push( to_add );\r\n });\r\n\r\n active_gradient = new_gradient + colors_part.join(', ') + ')';\r\n\r\n if(document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)')) {\r\n document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').style.background = active_gradient;\r\n }\r\n\r\n if(also_apply_changes) {\r\n this.apply_changes();\r\n }\r\n };\r\n\r\n\r\n\r\n /* apply changes to target field */\r\n this.apply_changes = function() {\r\n if(!active_trigger) {\r\n return false;\r\n }\r\n let val = '';\r\n\r\n // apply everything to picker global vars\r\n if(active_mode == 'solid') {\r\n val = this.shorten_hex(active_solid);\r\n\r\n if(options.transparency && document.querySelector('.pccp_opacity_f_wrap input[type=range]')) {\r\n active_opacity = document.querySelector('.pccp_opacity_f_wrap input[type=range]').value;\r\n val = this.hex_to_RGBA(val, active_opacity);\r\n }\r\n }\r\n else {\r\n val = active_gradient;\r\n }\r\n\r\n // apply\r\n active_trigger.style.background = val;\r\n\r\n const field = active_trigger.parentNode.querySelector(right_input_selector),\r\n old_val = field.value;\r\n\r\n if(old_val != val) {\r\n field.value = val;\r\n last_tracked_col = val;\r\n\r\n if(typeof(options.on_change) == 'function') {\r\n\r\n if(typeof(debounced_vars['on_change_cb']) != undefined && debounced_vars['on_change_cb']) {\r\n clearTimeout(debounced_vars['on_change_cb']);\r\n }\r\n debounced_vars['on_change_cb'] = setTimeout(() => {\r\n options.on_change.call(this, val, field);\r\n }, 300);\r\n }\r\n }\r\n };\r\n\r\n\r\n\r\n\r\n\r\n\r\n // HANDLERS\r\n\r\n // fields toggle basing on modes change\r\n this.mode_change = function(el, new_mode) {\r\n if(active_mode == new_mode) {\r\n return false;\r\n }\r\n let color, opacity;\r\n\r\n // from gradient to solid\r\n if(new_mode == 'solid') {\r\n color = active_solid;\r\n if(options.transparency) {\r\n opacity = active_opacity;\r\n }\r\n }\r\n else {\r\n color = gradient_data.steps[0].color;\r\n if(options.transparency) {\r\n opacity = gradient_data.steps[0].opacity;\r\n }\r\n }\r\n\r\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = color;\r\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = color;\r\n\r\n if(options.transparency) {\r\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = opacity;\r\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = opacity;\r\n }\r\n\r\n // toggle grad fields\r\n if(options.modes.length >= 1) {\r\n document.querySelector('.pccp_deg_f_wrap').style.display = (new_mode == 'linear-gradient') ? 'flex' : 'none';\r\n document.querySelector('.pccp_circle_f_wrap').style.display = (new_mode == 'radial-gradient') ? 'block' : 'none';\r\n }\r\n\r\n // toogle gradient wizard\r\n if(options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) {\r\n document.querySelector('.lccp_gradient_wizard').style.display = (new_mode != 'solid') ? 'block' : 'none';\r\n }\r\n\r\n document.querySelectorAll('#lccp_modes_wrap span').forEach(m => m.classList.remove('lccp_sel_mode'));\r\n el.classList.add('lccp_sel_mode');\r\n\r\n active_mode = new_mode;\r\n (new_mode == 'solid') ? this.apply_changes() : this.apply_gradient_changes(true);\r\n };\r\n\r\n\r\n // add gradient step\r\n this.add_gradient_step = function(e) {\r\n const $this = this,\r\n pos = Math.round((100 * e.layerX) / e.target.offsetWidth);\r\n\r\n // inject in actual steps\r\n let index = 0;\r\n for(let step of gradient_data.steps) {\r\n\r\n if(step.position > pos) {\r\n const step_data = {\r\n color : (index - 1 < 0) ? step.color : gradient_data.steps[(index - 1)].color,\r\n opacity : 1,\r\n position : pos\r\n }\r\n\r\n gradient_data.steps.splice(index, 0, step_data);\r\n break;\r\n }\r\n\r\n index++;\r\n }\r\n document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove());\r\n\r\n gradient_data.steps.some(function(step, index) {\r\n $this.add_draggable_element(index, step.position, step.color);\r\n });\r\n\r\n // select newly added element\r\n document.querySelector('.lccp_gradient_range[data-step-num=\"'+ index +'\"]').click();\r\n\r\n this.apply_gradient_changes(true);\r\n };\r\n\r\n\r\n // apply ellipse or circle\r\n this.set_ellipse_circle = function(el, new_opt) {\r\n if(gradient_data.radial_circle && new_opt == 'circle' || !gradient_data.radial_circle && new_opt != 'circle') {\r\n return false;\r\n }\r\n gradient_data.radial_circle = !gradient_data.radial_circle;\r\n\r\n document.querySelectorAll('.pccp_circle_f_wrap span').forEach(m => m.classList.remove('pcpp_circle_btn_active'));\r\n el.classList.add('pcpp_circle_btn_active');\r\n\r\n this.apply_gradient_changes(true);\r\n };\r\n\r\n\r\n // track opacity range fields change\r\n this.track_deg_range_change = function(e) {\r\n document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').value = e.target.value;\r\n\r\n gradient_data.deg = e.target.value;\r\n this.apply_gradient_changes(true);\r\n };\r\n this.track_deg_num_change = function(e) {\r\n let val = parseFloat(e.target.value);\r\n if(isNaN(val) || val < 0 || val > 360) {\r\n val = 90;\r\n }\r\n\r\n e.target.value = val;\r\n if(document.querySelector('.pccp_deg_f_wrap input[type=range]')) {\r\n document.querySelector('.pccp_deg_f_wrap input[type=range]').value = val;\r\n }\r\n\r\n gradient_data.deg = val;\r\n this.apply_gradient_changes(true);\r\n };\r\n\r\n\r\n // track opacity range fields change\r\n this.track_color_change = function(e) {\r\n const val = e.target.value.toLowerCase();\r\n document.querySelector('.pccp_color_f_wrap input[name=hex]').value = val;\r\n\r\n this.apply_color_change(val);\r\n };\r\n this.track_color_hex_change = function(e) {\r\n let val = this.short_hex_fix(e.target.value);\r\n\r\n if(val.match(/^#[a-f0-9]{6}$/i) === null) {\r\n val = active_solid.toLowerCase();\r\n }\r\n\r\n e.target.value = val;\r\n document.querySelector('#lc-color-picker input[type=\"color\"]').value = val;\r\n\r\n this.apply_color_change(val);\r\n };\r\n this.apply_color_change = function(val) {\r\n if(active_mode == 'solid') {\r\n active_solid = val;\r\n this.apply_changes();\r\n }\r\n else {\r\n gradient_data.steps[ sel_grad_step ].color = val;\r\n\r\n document.querySelector('.lccp_sel_step').style.background = val;\r\n this.apply_gradient_changes(true);\r\n }\r\n };\r\n\r\n\r\n // track opacity range fields change\r\n this.track_opacity_range_change = function(e) {\r\n document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = e.target.value;\r\n this.alter_hex_opacity(e.target.value);\r\n };\r\n this.track_opacity_num_change = function(e) {\r\n let val = parseFloat(e.target.value);\r\n if(isNaN(val) || val < 0 || val > 1) {\r\n val = 0.5;\r\n }\r\n\r\n e.target.value = val;\r\n\r\n if(document.querySelector('.pccp_opacity_f_wrap input[type=range]')) {\r\n document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = val;\r\n this.alter_hex_opacity(val);\r\n }\r\n };\r\n this.alter_hex_opacity = function(opacity) {\r\n document.querySelector('#lc-color-picker input[type=\"color\"]').style.opacity = opacity;\r\n\r\n if(active_mode == 'solid') {\r\n active_opacity = opacity;\r\n this.apply_changes();\r\n }\r\n else {\r\n gradient_data.steps[ sel_grad_step ].opacity = opacity;\r\n this.apply_gradient_changes(true);\r\n }\r\n };\r\n\r\n\r\n\r\n\r\n\r\n /*\r\n * UTILITY FUNCTION - debounce action to run once after X time\r\n *\r\n * @param (string) action_name\r\n * @param (int) timing - milliseconds to debounce\r\n * @param (string) - class method name to call after debouncing\r\n * @param (mixed) - extra parameters to pass to callback function\r\n */\r\n this.debounce = function(action_name, timing, cb_function, cb_params) {\r\n if( typeof(debounced_vars[ action_name ]) != 'undefined' && debounced_vars[ action_name ]) {\r\n clearTimeout(debounced_vars[ action_name ]);\r\n }\r\n const $this = this;\r\n\r\n debounced_vars[ action_name ] = setTimeout(() => {\r\n $this[cb_function].call($this, cb_params);\r\n }, timing);\r\n };\r\n\r\n\r\n\r\n\r\n\r\n /* CSS - creates inline CSS into the page */\r\n this.generate_style = function() {\r\n const transp_bg_img = \"url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkICgoMDAwMDAoMDA0NDAwRERERERQUFBQUFBQUFBT/2wBDAQQFBQgHCA8KCg8UDg4OFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAoACgDAREAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAUGCP/EACIQAQAABQMFAQAAAAAAAAAAAAAFFUNjgqLB4RESEzVRkf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDv4AE6K0stgTgAaIAE6K0stgTgAUZra1cATW1q4A9na8WXXu/PgEqu6eQJVd08gnAAowqrjuCiADOgAowqrjuCiAD/2Q==')\";\r\n\r\n document.head.insertAdjacentHTML('beforeend',\r\n``);\r\n };\r\n\r\n\r\n // init when called\r\n this.init();\r\n };\r\n\r\n\r\n\r\n\r\n\r\n\r\n // UTILITIES\r\n\r\n // sanitize \"selector\" parameter allowing both strings and DOM objects\r\n const maybe_querySelectorAll = (selector) => {\r\n\r\n if(typeof(selector) != 'string') {\r\n if(selector instanceof Element) { // JS or jQuery\r\n return [selector];\r\n }\r\n else {\r\n let to_return = [];\r\n\r\n for(const obj of selector) {\r\n if(obj instanceof Element) {\r\n to_return.push(obj);\r\n }\r\n }\r\n return to_return;\r\n }\r\n }\r\n\r\n // clean problematic selectors\r\n (selector.match(/(#[0-9][^\\s:,]*)/g) || []).forEach(function(n) {\r\n selector = selector.replace(n, '[id=\"' + n.replace(\"#\", \"\") + '\"]');\r\n });\r\n\r\n return document.querySelectorAll(selector);\r\n };\r\n\r\n\r\n})());\r\n"],"names":["global","factory","this","window","lc_color_picker","debounced_vars","window_width","style_generated","active_trigger","active_trig_id","active_solid","active_opacity","active_gradient","active_mode","sel_grad_step","gradient_data","deg","radial_circle","steps","def_opts","modes","open_on_focus","transparency","dark_theme","no_input_mode","wrap_width","preview_style","input_padding","side","width","separator_color","fallback_colors","on_change","labels","right_input_selector","lccp_ivc_event","picker_id","hide_picker","CustomEvent","bubbles","detail","document","addEventListener","e","picker","querySelector","target","classList","contains","trigger","getElementsByClassName","parentNode","getElementById","getAttribute","dispatchEvent","innerWidth","String","prototype","lccpReplaceArray","find","replace","replaceString","i","length","regex","RegExp","attachTo","cp_uniqid","last_tracked_col","options","console","error","bkp_opts","Object","assign","init","$this","generate_style","maybe_querySelectorAll","forEach","el","tagName","wrap_element","Math","random","toString","substr","side_prop","trigger_css","parseInt","getComputedStyle","trigger_upper_css","value","div","createElement","className","setAttribute","style","round","getBoundingClientRect","direct_colorpicker_code","add","innerHTML","insertBefore","appendChild","paddingRight","paddingLeft","background","show_picker","remove","debounce","keyCode","key","is_active_trigger_and_opened","activeElement","curr_val","test","val_to_set","browser_val","trim","replaceAll","rgb","RGB_to_hex","toLowerCase","indexOf","call","$target","direct_colorpicker","navigator","userAgent","includes","click","val_to_picker","append_color_picker","picker_w","offsetWidth","picker_h","offsetHeight","at_offsety","at_h","clientHeight","y_pos","y","pageYOffset","left","right","floor","y_pos_css","documentElement","scrollTop","innerHeight","from_manual_input","val","load_gradient_data","load_solid_data","raw_data","data","RGBA_to_hexA","short_hex_fix","is_radial","raw_steps","split","fallback_multiplier","some","raw_step","index","position","raw_color","opacity","col_arr","push","color","rgba_arr","alpha","substring","splice","join","parseFloat","rgb_arr","r","g","b","shorten_hex","hex","a","hex_to_RGB","h","hex_to_RGBA","on_manual_input_change","theme_class","shown_solid","shown_opacity","print_grad_code","picker_el","body","insertAdjacentHTML","mode","querySelectorAll","mode_change","step","add_draggable_element","add_gradient_step","track_deg_range_change","track_deg_num_change","set_ellipse_circle","track_color_change","track_opacity_range_change","track_opacity_num_change","rel_step_num","container","sel_class","del_btn_vis","active","dragStart","range_id","dragEnd","apply_changes","drag","range","preventDefault","rect","new_pos","type","touches","clientX","min_pos","max_pos","apply_gradient_changes","step_num","removeEventListener","select_gradient_color","btn","setTimeout","parent","to_select","m","also_apply_changes","new_gradient","colors_part","to_add","field","undefined","clearTimeout","new_mode","display","pos","layerX","step_data","new_opt","isNaN","apply_color_change","track_color_hex_change","match","alter_hex_opacity","action_name","timing","cb_function","cb_params","head","selector","Element","to_return","obj","n","exports","module","define","amd","globalThis","self","Chart"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/gradient.js b/amd/src/gradient.js new file mode 100644 index 0000000..3a3ae5f --- /dev/null +++ b/amd/src/gradient.js @@ -0,0 +1,3 @@ +define(['block_dash/lc_color_picker'], function(Picker) { + return Picker; +}); \ No newline at end of file diff --git a/amd/src/gradienthandler.js b/amd/src/gradienthandler.js new file mode 100644 index 0000000..84a4181 --- /dev/null +++ b/amd/src/gradienthandler.js @@ -0,0 +1,7 @@ +define([], function () { + return { + init: function() { + alert('demo'); + } + }; +}); diff --git a/amd/src/lc_color_picker.js b/amd/src/lc_color_picker.js new file mode 100644 index 0000000..b63acd4 --- /dev/null +++ b/amd/src/lc_color_picker.js @@ -0,0 +1,1665 @@ +/** + * lc_color_picker.js - The colorpicker for modern web + * Version: 2.0.0 + * Author: Luca Montanari (LCweb) + * Website: https://lcweb.it + * Licensed under the MIT license + */ + + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory()); + })(this, (function () { 'use strict'; + + if(typeof(window.lc_color_picker) != 'undefined') {return false;} // prevent multiple script inits + + + /*** vars ***/ + let debounced_vars = [], + window_width = null, + + style_generated = null, + active_trigger = null, + active_trig_id = null, + + active_solid = null, + active_opacity = null, + active_gradient = null, + active_mode = 'linear-gradient', + + sel_grad_step = 0, // selected gradient step + gradient_data = { + deg: 0, + radial_circle: false, + steps: [ + //{color : null, opacity: null, position : null} + ], + }; + + + /*** default options ***/ + const def_opts = { + modes : ['linear-gradient'], // (array) containing supported modes (solid | linear-gradient | radial-gradient) + open_on_focus : true, // (bool) whether to open the picker when field is focused + transparency : true, // (bool) whether to allow colors transparency tune + dark_theme : false, // (bool) whether to enable dark picker theme + no_input_mode : false, // (bool) whether to stretch the trigger in order to cover the whole input field + wrap_width : 'auto', // (string) defines the wrapper width. "auto" to leave it up to CSS, "inherit" to statically copy input field width, or any other CSS sizing + preview_style : { // (object) defining shape and position of the in-field preview + input_padding : 35, // extra px padding eventually added to the target input to not cover text + side : 'right', // right or left + width : 30, + separator_color : '#ccc', // (string) CSS color applird to preview element as separator + }, + fallback_colors : ['#008080', 'linear-gradient(90deg, #fff 0%, #000 100%)'], // (array) defining default colors used when trigger field has no value. First parameter for solid color, second for gradient + + on_change : null, // function(new_value, target_field) {}, - triggered every time field value changes. Passes value and target field object as parameters + + labels : [ // (array) option used to translate script texts + 'click to change color', + 'Solid', + 'Linear Gradient', + 'Radial Gradient', + 'add gradient step', + 'gradient angle', + 'gradient shape', + 'color', + 'opacity', + ], + }; + + + // shortcut var to target the text input only + const right_input_selector = 'input:not([type="color"])'; + + + + // input value check custom event + const lccp_ivc_event = function(picker_id, hide_picker = false) { + return new CustomEvent('lccp_input_val_check', { + bubbles : true, + detail: { + picker_id : picker_id, + hide_picker : hide_picker + } + }); + }; + + + + /*** hide picker cicking outside ***/ + document.addEventListener('click', function(e) { + const picker = document.querySelector("#lc-color-picker.lccp-shown"); + if(!picker || e.target.classList.contains('lccp-preview')) { + return true; + } + + // is an element within a trigger? + for (const trigger of document.getElementsByClassName('lccp-preview')) { + if(trigger.contains(e.target)) { + return true; + } + } + + // clicked on the same colorpicker field? keep visible + if(e.target.parentNode && e.target.parentNode.classList && e.target.parentNode.classList.contains('lccp-el-wrap') && document.getElementById(active_trig_id)) { + return true; + } + + // close if clicked element is not in the picker + if(!picker.contains(e.target) && !e.target.classList.contains('lccp-shown')) { + const picker_id = picker.getAttribute('data-trigger-id'), + $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector); + + $input.dispatchEvent(lccp_ivc_event(picker_id, true)); + } + return true; + }); + + + /* hide picker on screen resizing */ + window.addEventListener('resize', function(e) { + const picker = document.querySelector("#lc-color-picker.lccp-shown"); + if(!picker || window_width == window.innerWidth) { + return true; + } + + // check field value + const picker_id = picker.getAttribute('data-trigger-id'), + $input = document.getElementById(picker_id).parentNode.querySelector(right_input_selector); + + $input.dispatchEvent(lccp_ivc_event(picker_id, true)); + }); + + + /* extend string object to ReplaceArray */ + String.prototype.lccpReplaceArray = function(find, replace) { + let replaceString = this; + let regex; + + for (var i = 0; i < find.length; i++) { + const regex = new RegExp(find[i], "g"); + replaceString = (typeof(replace) == 'object') ? replaceString.replace(regex, replace[i]) : replaceString.replace(regex, replace); + } + return replaceString; + }; + + + + + /*** plugin class ***/ + window.lc_color_picker = function(attachTo, options = {}) { + let cp_uniqid, // unique ID assigned to this colorpicker instance + last_tracked_col; + + this.attachTo = attachTo; + if(!this.attachTo) { + return console.error('You must provide a valid selector string first argument'); + } + + // override options + if(typeof(options) != 'object') { + return console.error('Options must be an object'); + } + + const bkp_opts = options; + options = Object.assign({}, def_opts, options); + + if(typeof(bkp_opts.preview_style) != 'undefined') { + options.preview_style = Object.assign({}, def_opts.preview_style, bkp_opts.preview_style); + } + + + + /* initialize */ + this.init = function() { + const $this = this; + + // Generate style + if(!style_generated) { + this.generate_style(); + style_generated = true; + } + + + // assign to each target element + maybe_querySelectorAll(attachTo).forEach(function(el) { + if(el.tagName == 'INPUT' && el.getAttribute('type') != 'text') { + return; + } + + // do not initialize twice + if(el.parentNode.classList.length && el.parentNode.classList.contains('lcslt_wrap')) { + return; + } + + $this.wrap_element(el); + }); + }; + + + + /* wrap target element to allow trigger display */ + this.wrap_element = function(el) { + cp_uniqid = Math.random().toString(36).substr(2, 9); + + const $this = this, + side_prop = (options.preview_style.side == 'right') ? 'borderRightWidth' : 'borderLeftWidth'; + + let trigger_css = + `width:${ (options.no_input_mode) ? 'calc(100% - '+ parseInt(getComputedStyle(el)['borderRightWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderLeftWidth'], 10) +'px);' : options.preview_style.width +'px;'}` + + + options.preview_style.side +':'+ parseInt(getComputedStyle(el)[side_prop], 10) +'px;'+ + + 'top:'+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px;' + + + 'height: calc(100% - '+ parseInt(getComputedStyle(el)['borderTopWidth'], 10) +'px - '+ parseInt(getComputedStyle(el)['borderBottomWidth'], 10) +'px);'; + + let trigger_upper_css = + trigger_css + + 'background:'+ el.value +';' + + 'border-color:'+ options.preview_style.separator_color +';' + + let div = document.createElement('div'); + div.className = 'lccp-preview-'+ options.preview_style.side; + div.setAttribute('data-for', el.getAttribute('name')); + + // static width from input? + if(options.wrap_width != 'auto') { + div.style.width = (options.wrap_width == 'inherit') ? Math.round(el.getBoundingClientRect().width) + 'px' : options.wrap_width; + } + + const direct_colorpicker_code = (!options.transparency && options.modes.length == 1 && options.modes[0] == 'linear-gradient') ? + '' : ''; + + div.classList.add("lccp-el-wrap"); + div.innerHTML = + '' + + '' + + direct_colorpicker_code; + + el.parentNode.insertBefore(div, el); + div.appendChild(el); + + // input padding + if(!options.no_input_mode) { + if(options.preview_style.side == 'right') { + div.querySelector('input:not([type="color"])').style.paddingRight = options.preview_style.input_padding +'px'; + } else { + div.querySelector('input:not([type="color"])').style.paddingLeft = options.preview_style.input_padding +'px'; + } + } + + + // direct browser colorpicker? track changes + if(div.querySelector('.lccp-direct-cp-f')) { + div.querySelector('.lccp-direct-cp-f').addEventListener("input", (e) => { + + div.querySelector('input:not([type="color"])').value = e.target.value; + div.querySelector('.lccp-preview').style.background = e.target.value; + }); + } + + + // event to show picker + const trigger = document.getElementById(cp_uniqid); + trigger.addEventListener("click", (e) => { + this.show_picker(trigger); + }); + + + + // show on field focus? + if(options.open_on_focus) { + div.querySelector(right_input_selector).addEventListener("focus", (e) => { + if(trigger != active_trigger) { + if(active_trigger) { + document.getElementById('lc-color-picker').classList.remove('lccp-shown'); + active_trigger = null; + } + + $this.debounce('open_on_focus', 10, 'show_picker', trigger); + } + }); + } + + + // sync manually-inputed data in the field + div.querySelector(right_input_selector).addEventListener("keyup", (e) => { + if(e.keyCode == 9 || e.key === 'Enter' || e.keyCode === 13) { + return; + } + + const is_active_trigger_and_opened = (active_trig_id = cp_uniqid && document.querySelector("#lc-color-picker.lccp-shown")) ? true : false; + + active_trigger = trigger; + active_trig_id = cp_uniqid; + + $this.debounce('manual_input_sync', 10, 'val_to_picker', true); + + if(is_active_trigger_and_opened) { + $this.debounce('manual_input_sync_cp', 10, 'append_color_picker', false); + $this.debounce('reopen_picker_after_manual_edit', 10, 'show_picker', trigger); + } + }); + + + // be sure input value is managed on focusout + div.querySelector(right_input_selector).addEventListener("focusout", (e) => { + // not if this field's picker is shown and focus is on "body" + if(document.activeElement.tagName == 'BODY' && document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+ active_trig_id +'"]')) { + return true; + } + + e.target.dispatchEvent(lccp_ivc_event(active_trig_id, true)); + }); + + + // custom event - check field validity and eventually use fallback values + div.querySelector(right_input_selector).addEventListener("lccp_input_val_check", (e) => { + const curr_val = e.target.value, + test = document.createElement('div'); + + test.style.background = curr_val; + let browser_val = test.style.background, + val_to_set; + + if(!curr_val.trim().length || !browser_val) { + if(e.target.value.toLowerCase().indexOf('gradient') === -1) { + val_to_set = (options.fallback_colors[0].toLowerCase().indexOf('rgba') === -1) ? $this.RGB_to_hex(options.fallback_colors[0]) : options.fallback_colors[0]; + } + else { + val_to_set = options.fallback_colors[1]; + } + } + else { + // browser already fixes minor things + browser_val = browser_val.replaceAll('0.', '.').replace(/rgb\([^\)]+\)/g, (rgb) => { + return $this.RGB_to_hex(rgb); + }); + + val_to_set = (browser_val.trim().toLowerCase().substr(0, 4) == 'rgb(') ? $this.RGB_to_hex(browser_val) : browser_val; + } + + if(val_to_set != curr_val) { + e.target.value = val_to_set; + } + + if(typeof(options.on_change) == 'function' && last_tracked_col != val_to_set) { + options.on_change.call($this, val_to_set, e.target); + } + + if(e.detail.picker_id == active_trig_id) { + active_trigger = null; + active_trig_id = null; + } + + + // also hide picker? + const $target = document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+ e.detail.picker_id +'"]'); + if($target) { + + $target.classList.remove('lccp-shown'); + document.getElementById("lc-color-picker").remove(); + } + }); + }; + + + + /* show picker */ + this.show_picker = function(trigger) { + if(document.querySelector('#lc-color-picker.lccp-shown[data-trigger-id="'+ active_trig_id +'"]')) { + document.getElementById("lc-color-picker").remove(); + active_trigger = null; + active_trig_id = null + + return false; + } + + // direct colorpicker usage? Not for Firefox is "show on focus" is enabled + const direct_colorpicker = trigger.parentNode.querySelector('.lccp-direct-cp-f'); + if( + direct_colorpicker && + ( + !options.open_on_focus || + (options.open_on_focus && !navigator.userAgent.toLowerCase().includes('firefox')) + ) + ) { + direct_colorpicker.value = active_solid; + direct_colorpicker.click(); + return true; + } + + + window_width = window.innerWidth; + active_trigger = trigger; + active_trig_id = cp_uniqid; + + this.val_to_picker(); + this.append_color_picker(); + + const picker = document.getElementById('lc-color-picker'), + picker_w = picker.offsetWidth, + picker_h = picker.offsetHeight, + at_offsety = active_trigger.getBoundingClientRect(), + at_h = parseInt(active_trigger.clientHeight, 10) + parseInt(getComputedStyle(active_trigger)['borderTopWidth'], 10) + parseInt(getComputedStyle(active_trigger)['borderBottomWidth'], 10), + y_pos = (parseInt(at_offsety.y, 10) + parseInt(window.pageYOffset, 10) + at_h + 5); + + // left pos control - also checking side overflows + let left = (parseInt(at_offsety.right, 10) - picker_w); + if(left < 0) { + left = 0; + } + + // mobile? show it centered + if(window.innerWidth < 700) { + left = Math.floor( (window.innerWidth - picker_w) / 2); + } + + // top or bottom ? + const y_pos_css = (y_pos + picker_h - document.documentElement.scrollTop < window.innerHeight) ? + 'top:'+ y_pos : + 'transform: translate3d(0, calc((100% + '+ (active_trigger.offsetHeight + 10) +'px) * -1), 0); top:'+ y_pos; + + picker.setAttribute('style', y_pos_css +'px; left: '+ left +'px;'); + picker.classList.add('lccp-shown'); + }; + + + + /* handles input value and prepres data for the picker */ + this.val_to_picker = function(from_manual_input) { + if(!active_trigger) { + return false; + } + const val = active_trigger.parentNode.querySelector(right_input_selector).value.trim().toLowerCase(); + last_tracked_col = val; + + // check validity + let test = document.createElement('div'); + test.style.background = val; + + //// set active colors + // if no value found + if(!val.length || !test.style.background.length) { + active_solid = options.fallback_colors[0]; + active_gradient = options.fallback_colors[1]; + active_mode = 'linear-gradient'; + + /* if(val.indexOf('linear-gradient') !== -1) { + } + else if(val.indexOf('radial-gradient') !== -1) { + active_mode = 'radial-gradient'; + } + else { + active_mode = 'solid'; + } */ + } + else { + + active_mode = 'linear-gradient'; + active_gradient = val; + // find which value type has been passed + /* if(val.indexOf('linear-gradient') !== -1) { + } + else if(val.indexOf('radial-gradient') !== -1) { + active_mode = 'radial-gradient'; + } + else { + active_mode = 'solid'; + } + + if(active_mode == 'solid') { + active_solid = val; + active_gradient = options.fallback_colors[1]; + } + else{ + active_solid = options.fallback_colors[0]; + } */ + } + active_trigger.style.background = val; + + if(!from_manual_input || (from_manual_input && options.open_on_focus)) { + // elaborate solid color data (color and alpha) + //this.load_solid_data(active_solid); + // elaborate gradient data + if(active_gradient) { + this.load_gradient_data(active_gradient); + } + } + }; + + + + /* elaborate solid color data (color and alpha) loading into active_solid and active_opacity */ + this.load_solid_data = function(raw_data) { + active_opacity = 1; + + // rgba + if(raw_data.indexOf('rgba') !== -1) { + const data = this.RGBA_to_hexA(raw_data); + active_solid = data[0]; + active_opacity = data[1]; + } + + // rgb + else if(raw_data.indexOf('rgba') !== -1) { + active_solid = this.RGB_to_hex(raw_data); + } + + // hex + else { + active_solid = this.short_hex_fix(raw_data); + } + }; + + + + /* elaborate gradient data loading into gradient_data */ + this.load_gradient_data = function(raw_data) { + const $this = this; + const is_radial = (raw_data.indexOf('radial-gradient') === -1) ? false : true; + + // solve issues with inner RGB|RGBA and turn everything into RGBA + raw_data = raw_data + .replace(/,\./g, ',0.').replace(/ \./g, ' 0.') + .replace(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/g, 'rgbaZ($1|$2|$3|$4)') + .replace(/\|\)/g, '|1)'); + + // names to deg + raw_data = raw_data + .replace('top right', '45deg').replace('right top', '45deg') + .replace('bottom right', '135deg').replace('bottom right', '135deg') + .replace('top left', '315deg').replace('left top', '315deg') + .replace('bottom left', '225deg').replace('bottom left', '225deg') + .replace('right', '90deg').replace('left', '270deg').replace('top', '0deg').replace('bottom', '180deg'); + + // be sure deg or shape is defined + if(is_radial && raw_data.indexOf('ellipse') === -1 && raw_data.indexOf('circle') === -1) { + raw_data.replace('\\(', '(ellipse '); + } + if(!is_radial && raw_data.indexOf('deg') === -1) { + raw_data.replace('\\(', '(180deg'); + } + + // process + raw_data = raw_data.lccpReplaceArray( + ['linear-gradient', 'radial-gradient', '', '\\(', 'to', '\\)'], + '' + ); + + // split steps + const raw_steps = raw_data.split(','); + const fallback_multiplier = 100 / raw_steps.length; + + gradient_data.steps = []; + raw_steps.some(function(raw_step, index) { + + // direction on first index + if(!index) { + if(is_radial) { + gradient_data.radial_circle = (raw_step.indexOf('circle') === -1) ? false : true; + } else { + gradient_data.deg = parseInt(raw_step.replace('deg', ''), 10); + } + } + + // {color : null, opacity: null, position : null} + else { + raw_step = raw_step.trim().split(' '); + let position = ''; + + // position + if(raw_step.length < 2) { + if(index === 1) { + position = '0%'; + } + else if(index == (raw_steps.length - 1)) { + position = '100%'; + } + else { + position = (fallback_multiplier * index) +'%'; + } + } + else { + position = raw_step[1]; + } + + // color + let raw_color = raw_step[0], + opacity = 1; + + // normalize to hex + if(raw_color.indexOf('rgbaZ') !== -1) { + const col_arr = $this.RGBA_to_hexA( + raw_color.replace('rgbaZ', 'rgba').replace(/\|/g, ',') + ); + + raw_color = col_arr[0]; + opacity = col_arr[1]; + } + + gradient_data.steps.push({ + color : $this.short_hex_fix(raw_color), + opacity: opacity, + position : parseInt(position, 10) + }); + } + }); + }; + + + + /* handles RGBA string returning a two elements array: hex and alpha */ + this.RGBA_to_hexA = function(raw_data) { + raw_data = raw_data.lccpReplaceArray(['rgba', '\\(', '\\)'], ''); + const rgba_arr = raw_data.split(',') + + let alpha = (typeof(rgba_arr[3]) != 'undefined') ? rgba_arr[3] : '1'; + if(alpha.substring(0, 1) == '.') { + alpha = 0 + alpha; + } + rgba_arr.splice(3, 1); + + return [ + this.RGB_to_hex('rgb('+ rgba_arr.join(',') +')'), + parseFloat(alpha) + ]; + }; + + + + /* convert RGB to hex */ + this.RGB_to_hex = function(rgb) { + rgb = rgb.lccpReplaceArray(['rgb', '\\(', '\\)'], ''); + const rgb_arr = rgb.split(','); + + if(rgb_arr.length < 3) { + return ''; + } + + let r = parseInt(rgb_arr[0].trim(), 10).toString(16), + g = parseInt(rgb_arr[1].trim(), 10).toString(16), + b = parseInt(rgb_arr[2].trim(), 10).toString(16); + + if (r.length == 1) {r = "0" + r;} + if (g.length == 1) {g = "0" + g;} + if (b.length == 1) {b = "0" + b;} + + return this.shorten_hex(r + g + b); + }; + + + + /* if possible, shortenize hex string */ + this.shorten_hex = function(hex) { + hex = hex.replace('#', '').split(''); + + if(hex.length >= 6) { + if( + hex[0] === hex[1] && + hex[2] === hex[3] && + hex[4] === hex[5] + ) { + return '#'+ hex[0] + hex[2] + hex[4]; + } + } + + return '#'+ hex.join(''); + }; + + + + /* convert short hex to full format */ + this.short_hex_fix = function(hex) { + if(hex.length == 4) { + const a = hex.split(''); + hex = a[0] + a[1] + a[1] + a[2] + a[2] + a[3] + a[3]; + } + + return hex.toLowerCase(); + }; + + + + /* convert hex to RGB */ + this.hex_to_RGB = function(h) { + let r = 0, g = 0, b = 0; + + // 3 digits + if (h.length == 4) { + r = "0x" + h[1] + h[1]; + g = "0x" + h[2] + h[2]; + b = "0x" + h[3] + h[3]; + + // 6 digits + } else if (h.length == 7) { + r = "0x" + h[1] + h[2]; + g = "0x" + h[3] + h[4]; + b = "0x" + h[5] + h[6]; + } + + return "rgb("+ +r + ", " + +g + ", " + +b + ")"; + }; + + + + /* convert hex to RGB */ + this.hex_to_RGBA = function(h, opacity) { + if(parseFloat(opacity) === 1) { + return this.shorten_hex(h); + } + + let rgb = this.hex_to_RGB(h); + return rgb.replace('(', 'a(').replace(')', ', '+ opacity.toString().replace('0.', '.') +')'); + }; + + + + + /* append color container picker to the body */ + this.append_color_picker = function(on_manual_input_change = false) { + const $this = this; + + /* if(document.getElementById("lc-color-picker") && !on_manual_input_change) { + document.getElementById("lc-color-picker").remove(); + } */ + + const theme_class = (options.dark_theme) ? 'lccp_dark_theme' : 'lccp_light_theme', + bg = (active_mode == 'solid') ? active_solid : active_gradient, + shown_solid = (active_mode == 'solid') ? active_solid : gradient_data.steps[0].color, + shown_opacity = (active_mode == 'solid') ? active_opacity : (options.transparency) ? gradient_data.steps[0].opacity : null, + print_grad_code = (options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) ? true : false; + + + // start code + let picker = '', + picker_el; + + if(on_manual_input_change && document.getElementById("lc-color-picker")) { + picker_el = document.getElementById("lc-color-picker"); + picker_el.setAttribute('data-mode', active_mode); + picker_el.setAttribute('data-trigger-id', cp_uniqid); + } + else { + picker = '
'; + } + + + // modes select + /* if(options.modes.length >= 1) { + picker += ` +
+ ${ options.labels[1] } + ${ options.labels[2] } + ${ options.labels[3] } +
`; + } */ + + + // gradient wizard + if(print_grad_code) { + picker += ` +
+
+
+ +
+ +
+ angle + + + +
+
+ shape + + Ellipse + Circle +
+
+
`; + } + + + // HTML5 colorpicker + picker += ` +
+ color + +
+ +
+ +
`; + + // opacity cursor + if(options.transparency) { + picker += ` +
+ opacity + + + +
`; + } + + + // append or re-fill + (on_manual_input_change && document.getElementById("lc-color-picker")) ? picker_el.innerHTML = picker : document.body.insertAdjacentHTML('beforeend', picker +'
'); + + + // modes change + if(options.modes.length >= 1) { + for (const mode of document.querySelectorAll('#lccp_modes_wrap span')) { + mode.addEventListener("click", (e) => { $this.mode_change( e.target, e.target.getAttribute('data-mode')) }); + } + } + + // print steps and add gradient step action + if(print_grad_code) { + gradient_data.steps.some(function(step, index) { + $this.add_draggable_element(index, step.position, step.color); + }); + + document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').addEventListener("click", (e) => {this.add_gradient_step(e) }); + } + + // angle actions + if(options.modes.indexOf('linear-gradient') !== -1) { + document.querySelector('.pccp_deg_f_wrap input[type=range]').addEventListener("input", (e) => {this.track_deg_range_change(e)}); + document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener("change", (e) => {this.track_deg_num_change(e)}); + document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').addEventListener("keyup", (e) => { + this.debounce('deg_f_change', 500, 'track_deg_num_change', e); + }); + } + + // circle actions + if(options.modes.indexOf('radial-gradient') !== -1) { + for (const mode of document.querySelectorAll('.pccp_circle_f_wrap span')) { + mode.addEventListener("click", (e) => { $this.set_ellipse_circle( e.target, e.target.getAttribute('data-val')) }); + } + } + + // color actions + document.querySelector('.pccp_color_f_wrap input[type="color"]').addEventListener("input", (e) => {this.track_color_change(e)}); + document.querySelector('.pccp_color_f_wrap input[type="color"]').addEventListener("change", (e) => {this.track_color_change(e)}); + document.querySelector('.pccp_color_f_wrap input[name=hex]').addEventListener("keyup", (e) => { + this.debounce('hex_f_change', 600, 'track_color_hex_change', e); + }); + + // transparency actions + if(options.transparency) { + document.querySelector('.pccp_opacity_f_wrap input[type=range]').addEventListener("input", (e) => {this.track_opacity_range_change(e)}); + document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener("change", (e) => {this.track_opacity_num_change(e)}); + document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').addEventListener("keyup", (e) => { + this.debounce('opacity_f_change', 500, 'track_opacity_num_change', e); + }); + } + }; + + + + /*** add draggable element ***/ + this.add_draggable_element = function(rel_step_num, position, color) { + const $this = this, + container = document.querySelector('.lccp_gradient_ranges'), + sel_class = (!rel_step_num) ? 'lccp_sel_step' : '', + del_btn_vis = (gradient_data.steps.length > 2) ? '' : 'style="display: none;"' + + container.innerHTML += + ''+ + ''+ + ''; + + let active = false; + + ////// + const dragStart = function(range_id, el, e) { + active = range_id; + }; + + const dragEnd = function() { + active = false; + $this.apply_changes(); + }; + + const drag = function(range_id, range, e) { + if (active !== false && range_id == active) { + e.preventDefault(); + const rect = container.getBoundingClientRect(); + + let new_pos = (e.type === "touchmove") ? (e.touches[0].clientX - rect.left) : (e.clientX - rect.left); + new_pos = Math.round((100 * new_pos) / container.offsetWidth); + + if(new_pos < 0) {new_pos = 0;} + else if(new_pos > 100) {new_pos = 100;} + + // limit positions basing on previous and next step + const min_pos = (!range_id) ? 0 : gradient_data.steps[ range_id-1 ].position; + const max_pos = (range_id == (gradient_data.steps.length - 1)) ? 100 : gradient_data.steps[ range_id+1 ].position; + + if(new_pos < min_pos) {new_pos = min_pos + 1;} + else if(new_pos > max_pos) {new_pos = max_pos - 1;} + + gradient_data.steps[ range_id ].position = new_pos; + range.style.left = new_pos +'%'; + + $this.apply_gradient_changes(); + } + };+ + ///// + + document.querySelectorAll('.lccp_gradient_range').forEach(range => { + const step_num = parseInt(range.getAttribute('data-step-num'), 10); + + range.removeEventListener("touchstart", null); + range.removeEventListener("touchend", null); + range.removeEventListener("touchmove", null); + range.removeEventListener("click", null); + + range.removeEventListener("mousedown", null); + range.removeEventListener("mouseup", null); + + range.addEventListener("touchstart", (e) => {dragStart(step_num, e.target, e)}); + range.addEventListener("mousedown", (e) => {dragStart(step_num, e.target, e)}); + + range.addEventListener("click", (e) => {$this.select_gradient_color(step_num)}); + + container.addEventListener("touchmove", (e) => {drag(step_num, range, e)}); + container.addEventListener("mousemove", (e) => {drag(step_num, range, e)}); + + range.addEventListener("mouseup", (e) => {dragEnd()}); + range.addEventListener("touchend", (e) => {dragEnd()}); + document.addEventListener("mouseup", (e) => {dragEnd()}); + }); + + + // remove step handler + document.querySelectorAll('.lccp_gradient_range img').forEach((btn) => { + + btn.addEventListener("click", (e) => { + if(document.querySelectorAll('.lccp_gradient_range').length < 3) { + return false; + } + + // wait a bit to not interfere with global handler for picker closing + setTimeout(() => { + const parent = e.target.parentNode, + step_num = parseInt(parent.getAttribute('data-step-num'), 10), + to_select = (!step_num) ? 0 : step_num - 1; + + gradient_data.steps.splice(step_num, 1); + + // clean and restart + document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove()); + + gradient_data.steps.some(function(step, index) { + $this.add_draggable_element(index, step.position, step.color); + }); + + // select newly added element + document.querySelector('.lccp_gradient_range[data-step-num="'+ to_select +'"]').click(); + + this.apply_gradient_changes(true); + }, 20); + }); + }); + }; + + + + /* select gradient color */ + this.select_gradient_color = function(step_num) { + sel_grad_step = step_num; + + document.querySelectorAll('.lccp_gradient_range').forEach(m => m.classList.remove('lccp_sel_step')); + document.querySelector('.lccp_gradient_range[data-step-num="'+ step_num +'"]').classList.add('lccp_sel_step'); + + active_solid = gradient_data.steps[ step_num ].color; + active_opacity = gradient_data.steps[ step_num ].opacity; + + document.querySelector('#lc-color-picker input[type="color"]').value = active_solid; + document.querySelector('.pccp_color_f_wrap input[name=hex]').value = active_solid; + + if(options.transparency) { + document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = active_opacity; + document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = active_opacity; + } + }; + + + + /* apply changes to gradient, after a color/opacity/degree update */ + this.apply_gradient_changes = function(also_apply_changes) { + const $this = this; + + let new_gradient = active_mode+'('; + new_gradient += (active_mode == 'linear-gradient') ? gradient_data.deg+'deg' : (gradient_data.radial_circle) ? 'circle' : 'ellipse'; + new_gradient += ', '; + + let colors_part = [] + gradient_data.steps.some(function(step, index) { + + let to_add = (options.transparency) ? $this.hex_to_RGBA(step.color, step.opacity) : $this.shorten_hex(step.color); + + if( + gradient_data.steps.length > 2 || + ( + gradient_data.steps.length <= 2 && + ( + (!index && parseInt(step.position, 10)) || + (index && index < (gradient_data.steps.length - 1)) || + (index == (gradient_data.steps.length - 1) && parseInt(step.position, 10) != 100) + ) + ) + ) { + to_add += ' '+ step.position +'%'; + } + + colors_part.push( to_add ); + }); + + active_gradient = new_gradient + colors_part.join(', ') + ')'; + + if(document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)')) { + document.querySelector('.lccp_gradient:not(.lccp_gradient-bg)').style.background = active_gradient; + } + + if(also_apply_changes) { + this.apply_changes(); + } + }; + + + + /* apply changes to target field */ + this.apply_changes = function() { + if(!active_trigger) { + return false; + } + let val = ''; + + // apply everything to picker global vars + if(active_mode == 'solid') { + val = this.shorten_hex(active_solid); + + if(options.transparency && document.querySelector('.pccp_opacity_f_wrap input[type=range]')) { + active_opacity = document.querySelector('.pccp_opacity_f_wrap input[type=range]').value; + val = this.hex_to_RGBA(val, active_opacity); + } + } + else { + val = active_gradient; + } + + // apply + active_trigger.style.background = val; + + const field = active_trigger.parentNode.querySelector(right_input_selector), + old_val = field.value; + + if(old_val != val) { + field.value = val; + last_tracked_col = val; + + if(typeof(options.on_change) == 'function') { + + if(typeof(debounced_vars['on_change_cb']) != undefined && debounced_vars['on_change_cb']) { + clearTimeout(debounced_vars['on_change_cb']); + } + debounced_vars['on_change_cb'] = setTimeout(() => { + options.on_change.call(this, val, field); + }, 300); + } + } + }; + + + + + + + // HANDLERS + + // fields toggle basing on modes change + this.mode_change = function(el, new_mode) { + if(active_mode == new_mode) { + return false; + } + let color, opacity; + + // from gradient to solid + if(new_mode == 'solid') { + color = active_solid; + if(options.transparency) { + opacity = active_opacity; + } + } + else { + color = gradient_data.steps[0].color; + if(options.transparency) { + opacity = gradient_data.steps[0].opacity; + } + } + + document.querySelector('#lc-color-picker input[type="color"]').value = color; + document.querySelector('.pccp_color_f_wrap input[name=hex]').value = color; + + if(options.transparency) { + document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = opacity; + document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = opacity; + } + + // toggle grad fields + if(options.modes.length >= 1) { + document.querySelector('.pccp_deg_f_wrap').style.display = (new_mode == 'linear-gradient') ? 'flex' : 'none'; + document.querySelector('.pccp_circle_f_wrap').style.display = (new_mode == 'radial-gradient') ? 'block' : 'none'; + } + + // toogle gradient wizard + if(options.modes.indexOf('linear-gradient') !== -1 || options.modes.indexOf('radial-gradient') !== -1) { + document.querySelector('.lccp_gradient_wizard').style.display = (new_mode != 'solid') ? 'block' : 'none'; + } + + document.querySelectorAll('#lccp_modes_wrap span').forEach(m => m.classList.remove('lccp_sel_mode')); + el.classList.add('lccp_sel_mode'); + + active_mode = new_mode; + (new_mode == 'solid') ? this.apply_changes() : this.apply_gradient_changes(true); + }; + + + // add gradient step + this.add_gradient_step = function(e) { + const $this = this, + pos = Math.round((100 * e.layerX) / e.target.offsetWidth); + + // inject in actual steps + let index = 0; + for(let step of gradient_data.steps) { + + if(step.position > pos) { + const step_data = { + color : (index - 1 < 0) ? step.color : gradient_data.steps[(index - 1)].color, + opacity : 1, + position : pos + } + + gradient_data.steps.splice(index, 0, step_data); + break; + } + + index++; + } + document.querySelectorAll('.lccp_gradient_range').forEach(r => r.remove()); + + gradient_data.steps.some(function(step, index) { + $this.add_draggable_element(index, step.position, step.color); + }); + + // select newly added element + document.querySelector('.lccp_gradient_range[data-step-num="'+ index +'"]').click(); + + this.apply_gradient_changes(true); + }; + + + // apply ellipse or circle + this.set_ellipse_circle = function(el, new_opt) { + if(gradient_data.radial_circle && new_opt == 'circle' || !gradient_data.radial_circle && new_opt != 'circle') { + return false; + } + gradient_data.radial_circle = !gradient_data.radial_circle; + + document.querySelectorAll('.pccp_circle_f_wrap span').forEach(m => m.classList.remove('pcpp_circle_btn_active')); + el.classList.add('pcpp_circle_btn_active'); + + this.apply_gradient_changes(true); + }; + + + // track opacity range fields change + this.track_deg_range_change = function(e) { + document.querySelector('.pccp_deg_f_wrap input[name=deg-num]').value = e.target.value; + + gradient_data.deg = e.target.value; + this.apply_gradient_changes(true); + }; + this.track_deg_num_change = function(e) { + let val = parseFloat(e.target.value); + if(isNaN(val) || val < 0 || val > 360) { + val = 90; + } + + e.target.value = val; + if(document.querySelector('.pccp_deg_f_wrap input[type=range]')) { + document.querySelector('.pccp_deg_f_wrap input[type=range]').value = val; + } + + gradient_data.deg = val; + this.apply_gradient_changes(true); + }; + + + // track opacity range fields change + this.track_color_change = function(e) { + const val = e.target.value.toLowerCase(); + document.querySelector('.pccp_color_f_wrap input[name=hex]').value = val; + + this.apply_color_change(val); + }; + this.track_color_hex_change = function(e) { + let val = this.short_hex_fix(e.target.value); + + if(val.match(/^#[a-f0-9]{6}$/i) === null) { + val = active_solid.toLowerCase(); + } + + e.target.value = val; + document.querySelector('#lc-color-picker input[type="color"]').value = val; + + this.apply_color_change(val); + }; + this.apply_color_change = function(val) { + if(active_mode == 'solid') { + active_solid = val; + this.apply_changes(); + } + else { + gradient_data.steps[ sel_grad_step ].color = val; + + document.querySelector('.lccp_sel_step').style.background = val; + this.apply_gradient_changes(true); + } + }; + + + // track opacity range fields change + this.track_opacity_range_change = function(e) { + document.querySelector('.pccp_opacity_f_wrap input[name=opacity-num]').value = e.target.value; + this.alter_hex_opacity(e.target.value); + }; + this.track_opacity_num_change = function(e) { + let val = parseFloat(e.target.value); + if(isNaN(val) || val < 0 || val > 1) { + val = 0.5; + } + + e.target.value = val; + + if(document.querySelector('.pccp_opacity_f_wrap input[type=range]')) { + document.querySelector('.pccp_opacity_f_wrap input[type=range]').value = val; + this.alter_hex_opacity(val); + } + }; + this.alter_hex_opacity = function(opacity) { + document.querySelector('#lc-color-picker input[type="color"]').style.opacity = opacity; + + if(active_mode == 'solid') { + active_opacity = opacity; + this.apply_changes(); + } + else { + gradient_data.steps[ sel_grad_step ].opacity = opacity; + this.apply_gradient_changes(true); + } + }; + + + + + + /* + * UTILITY FUNCTION - debounce action to run once after X time + * + * @param (string) action_name + * @param (int) timing - milliseconds to debounce + * @param (string) - class method name to call after debouncing + * @param (mixed) - extra parameters to pass to callback function + */ + this.debounce = function(action_name, timing, cb_function, cb_params) { + if( typeof(debounced_vars[ action_name ]) != 'undefined' && debounced_vars[ action_name ]) { + clearTimeout(debounced_vars[ action_name ]); + } + const $this = this; + + debounced_vars[ action_name ] = setTimeout(() => { + $this[cb_function].call($this, cb_params); + }, timing); + }; + + + + + + /* CSS - creates inline CSS into the page */ + this.generate_style = function() { + const transp_bg_img = "url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAQDAwMDAwQDAwQGBAMEBgcFBAQFBwgGBgcGBggKCAkJCQkICgoMDAwMDAoMDA0NDAwRERERERQUFBQUFBQUFBT/2wBDAQQFBQgHCA8KCg8UDg4OFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAoACgDAREAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAUGCP/EACIQAQAABQMFAQAAAAAAAAAAAAAFFUNjgqLB4RESEzVRkf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwDv4AE6K0stgTgAaIAE6K0stgTgAUZra1cATW1q4A9na8WXXu/PgEqu6eQJVd08gnAAowqrjuCiADOgAowqrjuCiAD/2Q==')"; + + document.head.insertAdjacentHTML('beforeend', +``); + }; + + + // init when called + this.init(); + }; + + + + + + + // UTILITIES + + // sanitize "selector" parameter allowing both strings and DOM objects + const maybe_querySelectorAll = (selector) => { + + if(typeof(selector) != 'string') { + if(selector instanceof Element) { // JS or jQuery + return [selector]; + } + else { + let to_return = []; + + for(const obj of selector) { + if(obj instanceof Element) { + to_return.push(obj); + } + } + return to_return; + } + } + + // clean problematic selectors + (selector.match(/(#[0-9][^\s:,]*)/g) || []).forEach(function(n) { + selector = selector.replace(n, '[id="' + n.replace("#", "") + '"]'); + }); + + return document.querySelectorAll(selector); + }; + + +})()); diff --git a/block_dash.php b/block_dash.php index a0ef227..e4e7dbc 100644 --- a/block_dash.php +++ b/block_dash.php @@ -89,6 +89,15 @@ public function specialization() { // Configured datasource is missing. $this->title = get_string('newblock', 'block_dash'); } + + $showheader = get_config('block_dash', 'showheader'); + if (isset($this->config->showheader)) { + $showheader = $this->config->showheader; + } + + if (!$showheader && !$this->page->user_is_editing()) { + $this->title = ""; + } } /** @@ -112,6 +121,18 @@ public function instance_config_save($data, $nolongerused = false) { file_save_draft_area_files($data->backgroundimage, $this->context->id, 'block_dash', 'images', 0, ['subdirs' => 0, 'maxfiles' => 1]); } + if (isset($data->dash_configure_options) && isset($data->data_source_idnumber)) { + $datasource = data_source_factory::build_data_source($data->data_source_idnumber, + $this->context); + if ($datasource) { + if (method_exists($datasource, 'set_default_preferences')) { + $configpreferences = ['config_preferences' => []]; + $datasource->set_default_preferences($configpreferences); + $data->preferences = $configpreferences['config_preferences']; + } + } + unset($data->dash_configure_options); + } parent::instance_config_save($data, $nolongerused); } @@ -185,13 +206,22 @@ public function get_content() { $this->content->text = is_siteadmin() ? get_string('disableallmessage', 'block_dash') : ''; return $this->content; } + try { $bb = block_builder::create($this); + if (!$bb->get_configuration()) { + return $this->content->text = get_string('missingdatasource', 'block_dash'); + } + $datasource = $bb->get_configuration()->get_data_source(); // Conditionally hide the block when empty. - if ($datasource && isset($this->config->hide_when_empty) && $this->config->hide_when_empty - && (($datasource->is_widget() && $datasource->is_empty()) + $hidewhenempty = get_config('block_dash', 'hide_when_empty'); + if (isset($this->config->hide_when_empty)) { + $hidewhenempty = $this->config->hide_when_empty; + } + + if ($datasource && $hidewhenempty && (($datasource->is_widget() && $datasource->is_empty()) || (!$datasource->is_widget() && $datasource->get_data()->is_empty())) && !$this->page->user_is_editing()) { return $this->content; @@ -219,7 +249,14 @@ public function get_content() { public function html_attributes() { $attributes = parent::html_attributes(); if (isset($this->config->css_class)) { - $attributes['class'] .= ' ' . $this->config->css_class; + $cssclasses = $this->config->css_class; + if (!is_array($cssclasses)) { + $cssclasses = explode(',', $cssclasses); + } + foreach ($cssclasses as $class) { + $attributes['class'] .= ' ' . trim($class); + } + } if (isset($this->config->width)) { $attributes['class'] .= ' dash-block-width-' . $this->config->width; @@ -269,7 +306,21 @@ public function get_extra_css() { $blockcss[] = sprintf('background-image: url(%s);', $this->get_background_image_url()); } } else if ($backgroundgradient) { - $blockcss[] = sprintf('background: %s', $this->config->backgroundgradient); + $blockcss[] = sprintf('background-image: %s;', $this->config->backgroundgradient); + } + + // Background postition. + if (isset($this->config->backgroundimage_position)) { + $bgpostion = $this->config->backgroundimage_position; + $bgpostionvalue = ($bgpostion == 'custom') ? $this->config->backgroundimage_customposition : $bgpostion; + $blockcss[] = sprintf('background-position: %s;', $bgpostionvalue); + } + + // Background size. + if (isset($this->config->backgroundimage_size)) { + $bgsize = $this->config->backgroundimage_size; + $bgsizevalue = ($bgsize == 'custom') ? $this->config->backgroundimage_customsize : $bgsize; + $blockcss[] = sprintf('background-size: %s;', $bgsizevalue); } if (isset($this->config->css) && is_array($this->config->css)) { @@ -280,6 +331,16 @@ public function get_extra_css() { } } + if (isset($this->config->border_option)) { + if ($this->config->border_option) { + $bordervalue = isset($this->config->border) && ($this->config->border) ? $this->config->border + : "1px solid rgba(0,0,0,.125)"; + $blockcss[] = sprintf('%s: %s;', 'border', $bordervalue); + } else { + $blockcss[] = sprintf('%s: %s;', 'border', "none"); + } + } + $data['blockcss'] = implode(PHP_EOL, $blockcss); return $OUTPUT->render_from_template('block_dash/extra_css', $data); diff --git a/changes.md b/changes.md index d5eb1da..709d5fa 100644 --- a/changes.md +++ b/changes.md @@ -118,4 +118,4 @@ Compatibility: Moodle 3.7, Moodle 3.8, Totara (12) - Dashboards - Dashboards data source -Compatibility: Moodle 3.7, Moodle 3.8, Totara (12) \ No newline at end of file +Compatibility: Moodle 3.7, Moodle 3.8, Totara (12) diff --git a/classes/external.php b/classes/external.php index 22d33cd..04cfdca 100644 --- a/classes/external.php +++ b/classes/external.php @@ -57,8 +57,8 @@ public static function get_block_content_parameters() { 'page' => new \external_value(PARAM_INT, 'Paginator page.', VALUE_DEFAULT, 0), 'sort_field' => new \external_value(PARAM_TEXT, 'Field to sort by', VALUE_DEFAULT, null), 'sort_direction' => new \external_value(PARAM_TEXT, 'Sort direction of field', VALUE_DEFAULT, null), - 'pagelayout' => new \external_value(PARAM_TEXT, 'pagelayout', VALUE_OPTIONAL), - 'pagecontext' => new \external_value(PARAM_INT, 'Page Context', VALUE_OPTIONAL), + 'pagelayout' => new \external_value(PARAM_TEXT, 'pagelayout', VALUE_DEFAULT, ''), + 'pagecontext' => new \external_value(PARAM_INT, 'Page Context', VALUE_DEFAULT, 0), ]); } @@ -103,10 +103,10 @@ public static function get_block_content($blockinstanceid, $filterformdata, $pag $public = false; $blockinstance = $DB->get_record('block_instances', ['id' => $params['block_instance_id']]); $block = block_instance($blockinstance->blockname, $blockinstance); - if (strpos($block->instance->pagetypepattern, 'local-dash-dashboard') !== false) { - if ($dashboard = \local_dash\model\dashboard::get_record( + if (strpos($block->instance->pagetypepattern, 'dashaddon-dashboard') !== false) { + if ($dashboard = \dashaddon_dashboard\model\dashboard::get_record( ['shortname' => $block->instance->defaultregion])) { - if ($dashboard->get('permission') == \local_dash\model\dashboard::PERMISSION_PUBLIC) { + if ($dashboard->get('permission') == \dashaddon_dashboard\model\dashboard::PERMISSION_PUBLIC) { $public = true; } } diff --git a/classes/local/block_builder.php b/classes/local/block_builder.php index a92284c..4e07d00 100644 --- a/classes/local/block_builder.php +++ b/classes/local/block_builder.php @@ -116,10 +116,8 @@ public function is_section_expand_content_addon() { * @throws \moodle_exception */ public function get_block_content() { - // @codingStandardsIgnoreStart + global $OUTPUT, $CFG; - // Ignore the phplint due to block class not allowed to include the PAGE global variable. - // @codingStandardsIgnoreEnd /** @var renderer $renderer */ $renderer = $this->blockinstance->page->get_renderer('block_dash'); diff --git a/classes/local/configuration/configuration.php b/classes/local/configuration/configuration.php index d591020..d102b50 100644 --- a/classes/local/configuration/configuration.php +++ b/classes/local/configuration/configuration.php @@ -47,7 +47,7 @@ public static function create_from_instance(\block_base $blockinstance) { if (isset($blockinstance->config->data_source_idnumber)) { if (!$datasource = data_source_factory::build_data_source($blockinstance->config->data_source_idnumber, $parentcontext)) { - throw new \coding_exception('Missing data source.'); + return false; } if (isset($blockinstance->config->preferences) diff --git a/classes/local/dash_framework/structure/field.php b/classes/local/dash_framework/structure/field.php index 0c7c011..fb3e9ed 100644 --- a/classes/local/dash_framework/structure/field.php +++ b/classes/local/dash_framework/structure/field.php @@ -99,6 +99,7 @@ class field implements field_interface { * @param array $attributes Field attributes to be added immediately. * @param array $options Arbitrary options belonging to this field. * @param int $visibility Visibility of the field (if it should be displayed to the user). + * @param string $sortselect */ public function __construct(string $name, lang_string $title, diff --git a/classes/local/dash_framework/structure/user_table.php b/classes/local/dash_framework/structure/user_table.php index 7bcc026..4d04953 100644 --- a/classes/local/dash_framework/structure/user_table.php +++ b/classes/local/dash_framework/structure/user_table.php @@ -34,6 +34,7 @@ use block_dash\local\data_grid\field\attribute\moodle_url_attribute; use block_dash\local\data_grid\field\attribute\rename_group_ids_attribute; use block_dash\local\data_grid\field\attribute\user_image_url_attribute; +use block_dash\local\data_grid\field\attribute\bool_attribute; use lang_string; use moodle_url; @@ -143,11 +144,27 @@ public function get_fields(): array { $i = 0; foreach (profile_get_custom_fields() as $customfield) { - $fields[] = new field('pf_' . strtolower($customfield->shortname), + $name = 'pf_' . strtolower($customfield->shortname); + $profileattributes = []; + + switch ($customfield->datatype) { + case 'checkbox': + $profileattributes[] = new bool_attribute(); + break; + case 'datetime': + $profileattributes[] = new date_attribute(); + break; + case 'textarea': + break; + } + + $fields[] = new field( + $name, new lang_string('customfield', 'block_dash', ['name' => format_string($customfield->name)]), - $this, "(SELECT profile$i.data FROM {user_info_data} profile$i + $this, + "(SELECT profile$i.data FROM {user_info_data} profile$i WHERE profile$i.userid = u.id AND profile$i.fieldid = $customfield->id)", - [], [],field_interface::VISIBILITY_VISIBLE , '', + $profileattributes, [], field_interface::VISIBILITY_VISIBLE , '', ); $i++; diff --git a/classes/local/data_grid/field/attribute/category_image_url_attribute.php b/classes/local/data_grid/field/attribute/category_image_url_attribute.php new file mode 100644 index 0000000..b40525f --- /dev/null +++ b/classes/local/data_grid/field/attribute/category_image_url_attribute.php @@ -0,0 +1,112 @@ +. + +/** + * Generate the category image url from the fetched record. + * + * @package block_dash + * @copyright 2024 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace block_dash\local\data_grid\field\attribute; + +use block_dash\local\data_grid\field\attribute\abstract_field_attribute; + +/** + * Transform data to URL of category image. + * + * @package block_dash + */ +class category_image_url_attribute extends abstract_field_attribute { + + /** + * After records are relieved from database each field has a chance to transform the data. + * Example: Convert unix timestamp into a human readable date format + * + * @param \stdClass $data + * @param \stdClass $record Entire row + * @return mixed + * @throws \moodle_exception + */ + public function transform_data($data, \stdClass $record) { + global $CFG, $OUTPUT; + + require_once("$CFG->dirroot/blocks/dash/lib.php"); + + // Data is not integer then use the cc_id. + if (!is_int($data)) { + $data = $record->cc_id ?? 0; + } + + $files = $this->get_category_files(); + + if (!isset($files[$data]) && !isset($files[0])) { + return $OUTPUT->image_url('courses', 'block_myoverview'); + } + + // Verify the category images are added for the category or the fallback image is uploaded then use that. + if (isset($files[$data]) || isset($files[0])) { + + // Category imaage or fallback image. + $file = $files[$data] ?? $files[0]; + // Generate the URL. + $fileurl = \moodle_url::make_pluginfile_url( + $file->get_contextid(), + $file->get_component(), + $file->get_filearea(), + $file->get_itemid(), + $file->get_filepath(), + $file->get_filename(), false + ); + + return $fileurl->out(false); + } + + return $data; + } + + /** + * Get the list of category images. + * + * @return array|null + */ + public function get_category_files() { + + static $list = null; + + if ($list == null) { + // Get the system context. + $systemcontext = \context_system::instance(); + + // File storage. + $fs = get_file_storage(); + + // Get all files from category image filearea. + $files = $fs->get_area_files($systemcontext->id, 'block_dash', 'categoryimg', false, 'itemid', false); + + $list = []; + // Update the files index as itemid. + if (!empty($files)) { + foreach ($files as $id => $file) { + $list[$file->get_itemid()] = $file; + } + } + } + + return $list; + } +} diff --git a/classes/local/data_grid/field/attribute/category_recent_course_attribute.php b/classes/local/data_grid/field/attribute/category_recent_course_attribute.php new file mode 100644 index 0000000..9e80662 --- /dev/null +++ b/classes/local/data_grid/field/attribute/category_recent_course_attribute.php @@ -0,0 +1,54 @@ +. + +/** + * Generate the categories recent courses from the record. + * + * @package block_dash + * @copyright 2024 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace block_dash\local\data_grid\field\attribute; + +use block_dash\local\data_grid\field\attribute\abstract_field_attribute; + +/** + * Find the recent course of the categories. + * + * @package block_dash + */ +class category_recent_course_attribute extends abstract_field_attribute { + + /** + * After records are relieved from database each field has a chance to transform the data. + * Example: Convert unix timestamp into a human readable date format + * + * @param \stdClass $data + * @param \stdClass $record Entire row + * @return mixed + * @throws \moodle_exception + */ + public function transform_data($data, \stdClass $record) { + global $DB; + + if ($course = $DB->get_records('course', ['category' => $data], 'timecreated DESC', 'id, fullname', 0, 1)) { + return format_string(current($course)->fullname); + } + + return '-'; + } +} diff --git a/classes/local/data_grid/field/attribute/date_attribute.php b/classes/local/data_grid/field/attribute/date_attribute.php index 5687c1a..7eb2866 100644 --- a/classes/local/data_grid/field/attribute/date_attribute.php +++ b/classes/local/data_grid/field/attribute/date_attribute.php @@ -41,7 +41,8 @@ class date_attribute extends abstract_field_attribute { */ public function transform_data($data, \stdClass $record) { if (is_numeric($data) && $data > 0) { - return userdate($data, get_string('strftimedatefullshort')); + $format = $this->get_option('format') ?: get_string('strftimedatefullshort'); + return userdate($data, $format); } return null; diff --git a/classes/local/data_grid/field/attribute/image_attribute.php b/classes/local/data_grid/field/attribute/image_attribute.php index 112913f..2b2a2ec 100644 --- a/classes/local/data_grid/field/attribute/image_attribute.php +++ b/classes/local/data_grid/field/attribute/image_attribute.php @@ -39,6 +39,15 @@ class image_attribute extends abstract_field_attribute { * @return mixed */ public function transform_data($data, \stdClass $record) { + + if (($url = $this->get_option('customurl')) && ($labelfield = $this->get_option('label_field'))) { + $url = $this->update_placeholders($record, urldecode($url)); + return \html_writer::img($url, $this->get_option('title'), [ + 'class' => 'img-responsive', + 'role' => 'presentation ', + ]); + } + if ($data) { return \html_writer::img($data, $this->get_option('title'), [ 'class' => 'img-responsive', diff --git a/classes/local/data_grid/field/attribute/widget_attribute.php b/classes/local/data_grid/field/attribute/widget_attribute.php index ae8d4e6..cab6bee 100644 --- a/classes/local/data_grid/field/attribute/widget_attribute.php +++ b/classes/local/data_grid/field/attribute/widget_attribute.php @@ -43,12 +43,17 @@ public function transform_data($data, \stdClass $record) { global $PAGE, $DB; $widget = $this->get_option('widget'); $method = $this->get_option('method'); + $callback = $this->get_option('callback'); if ($widget && $method) { if (method_exists($widget, $method)) { $data = $widget->$method($record); } } + if ($callback) { + $data = $callback($record, $data); + } + return $data; } } diff --git a/classes/local/data_grid/field/field_definition_factory.php b/classes/local/data_grid/field/field_definition_factory.php index 7416f4d..7fac943 100644 --- a/classes/local/data_grid/field/field_definition_factory.php +++ b/classes/local/data_grid/field/field_definition_factory.php @@ -26,6 +26,7 @@ use block_dash\local\dash_framework\structure\field_interface; use block_dash\local\data_grid\field\attribute\field_attribute_interface; + /** * Responsible for building field definitions and retrieving them as needed. * diff --git a/classes/local/data_grid/field/field_definition_factory_interface.php b/classes/local/data_grid/field/field_definition_factory_interface.php index 83b30b2..a9690d5 100644 --- a/classes/local/data_grid/field/field_definition_factory_interface.php +++ b/classes/local/data_grid/field/field_definition_factory_interface.php @@ -23,6 +23,7 @@ */ namespace block_dash\local\data_grid\field; + /** * Responsible for creating field definitions on request. * diff --git a/classes/local/data_grid/filter/participants_condition.php b/classes/local/data_grid/filter/participants_condition.php index 4d4584c..91bc33b 100644 --- a/classes/local/data_grid/filter/participants_condition.php +++ b/classes/local/data_grid/filter/participants_condition.php @@ -59,7 +59,7 @@ public function get_values() { $coursecontext = \context_course::instance($course->id); if (has_capability('moodle/grade:viewall', $coursecontext)) { if (has_capability('moodle/site:accessallgroups', $coursecontext)) { - $users = array_merge($users, get_enrolled_users($coursecontext)); + $users = array_merge($users, get_users_by_capability($coursecontext, 'mod/assign:submit')); } else { $groups = groups_get_all_groups($course->id, $USER->id); if ($groupids = array_keys($groups)) { @@ -70,7 +70,7 @@ public function get_values() { } foreach ($users as $user) { - if ($user->id != $USER->id) { + if (($user->id != $USER->id) && (!is_siteadmin($user->id))) { $this->values[] = $user->id; } } diff --git a/classes/local/data_source/abstract_data_source.php b/classes/local/data_source/abstract_data_source.php index 72eb64f..b3ffdab 100644 --- a/classes/local/data_source/abstract_data_source.php +++ b/classes/local/data_source/abstract_data_source.php @@ -360,12 +360,12 @@ final public function get_data() { if (!$strategy = $this->get_layout()->get_data_strategy()) { throw new coding_exception('Not fully configured.'); } + if ($this->is_widget()) { $this->data = $this->get_widget_data(); } else { $records = $this->get_query()->query(); $this->data = $strategy->convert_records_to_data_collection($records, $this->get_sorted_fields()); - if ($modifieddata = $this->after_data($this->data)) { $this->data = $modifieddata; } diff --git a/classes/local/data_source/data_source_factory.php b/classes/local/data_source/data_source_factory.php index 8c31e58..1d6e7d7 100644 --- a/classes/local/data_source/data_source_factory.php +++ b/classes/local/data_source/data_source_factory.php @@ -153,7 +153,6 @@ public static function build_data_source($identifier, \context $context) { */ public static function get_data_source_form_options($type='') { $options = []; - foreach (self::get_data_source_registry() as $identifier => $datasourceinfo) { if ($type) { if (isset($datasourceinfo['type']) && $datasourceinfo['type'] == $type) { diff --git a/classes/local/data_source/data_source_interface.php b/classes/local/data_source/data_source_interface.php index cd64697..80ab293 100644 --- a/classes/local/data_source/data_source_interface.php +++ b/classes/local/data_source/data_source_interface.php @@ -70,6 +70,7 @@ public function get_data(); */ public function after_data(data_collection_interface $datacollection); + /** * Explicitly set layout. * diff --git a/classes/local/data_source/users_data_source.php b/classes/local/data_source/users_data_source.php index 0a9d896..f7da1db 100644 --- a/classes/local/data_source/users_data_source.php +++ b/classes/local/data_source/users_data_source.php @@ -41,6 +41,7 @@ use block_dash\local\data_grid\filter\user_field_filter; use block_dash\local\data_grid\filter\user_profile_field_filter; use block_dash\local\data_grid\filter\current_course_condition; +use block_dash\local\data_grid\filter\bool_filter; use coding_exception; use context; /** @@ -78,7 +79,7 @@ public function get_name() { * @throws coding_exception */ public function get_query_template(): builder { - global $CFG; + global $CFG, $DB; require_once("$CFG->dirroot/user/profile/lib.php"); @@ -151,15 +152,46 @@ public function build_filter_collection() { if (block_dash_has_pro()) { $filtercollection->add_filter(new \local_dash\data_grid\filter\parent_role_condition('parentrole', 'u.id')); + $filtercollection->add_filter(new \local_dash\data_grid\filter\cohort_condition('cohort', 'u.id')); + $filtercollection->add_filter(new \local_dash\data_grid\filter\users_mycohort_condition('users_mycohort', 'u.id')); } foreach (profile_get_custom_fields() as $field) { $alias = 'u_pf_' . strtolower($field->shortname); - $filter = new user_profile_field_filter($alias, $alias . '.data', $field->id, $field->name); - $filter->set_label(format_string($field->name)); - $filtercollection->add_filter($filter); + $select = $alias . '.data'; + switch ($field->datatype) { + case 'checkbox': + $definitions[] = new bool_filter($alias, $select, $field->name); + break; + case 'datetime': + $filtercollection->add_filter(new date_filter($alias, $select, date_filter::DATE_FUNCTION_FLOOR, + $field->name)); + break; + case 'textarea': + break; + default: + $filter = new user_profile_field_filter($alias, $alias . '.data', $field->id, $field->name); + $filter->set_label(format_string($field->name)); + $filtercollection->add_filter($filter); + break; + } } return $filtercollection; } + + /** + * Set the default preferences of the User datasource, force the set the default settings. + * + * @param array $data + * @return array + */ + public function set_default_preferences(&$data) { + $configpreferences = $data['config_preferences']; + $configpreferences['available_fields']['u_firstname']['visible'] = true; + $configpreferences['available_fields']['u_lastname']['visible'] = true; + $configpreferences['available_fields']['u_email']['visible'] = true; + $configpreferences['available_fields']['u_lastlogin']['visible'] = true; + $data['config_preferences'] = $configpreferences; + } } diff --git a/classes/local/paginator.php b/classes/local/paginator.php index 5f9e031..bd8e651 100644 --- a/classes/local/paginator.php +++ b/classes/local/paginator.php @@ -247,7 +247,8 @@ public function export_for_template(renderer_base $output) { 'total' => $this->get_record_count(), 'per_page' => $this->get_per_page(), 'limit_from' => $this->get_limit_from() + 1, - 'limit_to' => $limitto]); + 'limit_to' => $limitto, + ]); return [ 'pages' => $items, diff --git a/classes/local/widget/contacts/contacts_widget.php b/classes/local/widget/contacts/contacts_widget.php index a35379e..9ce52be 100644 --- a/classes/local/widget/contacts/contacts_widget.php +++ b/classes/local/widget/contacts/contacts_widget.php @@ -100,8 +100,13 @@ public function build_widget() { AND mua.id is NULL GROUP BY m.useridfrom'; $unreadcounts = $DB->get_records_sql($unreadcountssql, - [$userid, \core_message\api::MESSAGE_ACTION_READ, \core_message\api::MESSAGE_ACTION_DELETED, - $userid, $userid] + [ + $userid, + \core_message\api::MESSAGE_ACTION_READ, + \core_message\api::MESSAGE_ACTION_DELETED, + $userid, + $userid, + ] ); } else { $unreadcountssql = 'SELECT useridfrom, count(*) as count diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 2074f7f..65c7dbd 100644 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -104,7 +104,8 @@ protected function get_mustache() { // Don't allow the JavaScript helper to be executed from within another // helper. If it's allowed it can be used by users to inject malicious // JS into the page. - 'blacklistednestedhelpers' => ['js']]); + 'blacklistednestedhelpers' => ['js'], + ]); } diff --git a/classes/table/members.php b/classes/table/members.php index 22b00fb..4b82bf1 100644 --- a/classes/table/members.php +++ b/classes/table/members.php @@ -36,6 +36,13 @@ */ class members extends \table_sql implements dynamic_table { + /** + * Group filter value. + * + * @var string + */ + protected $group; + /** * Define table field definitions and filter data * diff --git a/edit_form.php b/edit_form.php index 2bbe14c..a672387 100644 --- a/edit_form.php +++ b/edit_form.php @@ -42,6 +42,7 @@ class block_dash_edit_form extends block_edit_form { * @throws coding_exception */ protected function specific_definition($mform) { + global $CFG; // Fields for editing HTML block title and contents. $mform->addElement('header', 'dashconfigheader', get_string('blocksettings', 'block')); @@ -62,6 +63,14 @@ protected function specific_definition($mform) { $mform->addElement('header', 'apperance', get_string('appearance')); + $mform->addElement('select', 'config_showheader', get_string('showheader', 'block_dash'), [ + 0 => get_string('hidden', 'block_dash'), + 1 => get_string('visible'), + ]); + $mform->setType('config_showheader', PARAM_INT); + $mform->setDefault('config_showheader', get_config('block_dash', 'showheader')); + $mform->addHelpButton('config_showheader', 'showheader', 'block_dash'); + $mform->addElement('select', 'config_width', get_string('blockwidth', 'block_dash'), [ 100 => '100', 50 => '1/2', @@ -82,25 +91,93 @@ protected function specific_definition($mform) { $mform->setType('config_hide_when_empty', PARAM_INT); $mform->setDefault('config_hide_when_empty', get_config('block_dash', 'hide_when_empty')); - $mform->addElement('text', 'config_css_class', get_string('cssclass', 'block_dash')); + $attributes['tags'] = true; + $attributes['multiple'] = 'multiple'; + $attributes['placeholder'] = get_string('enterclasses', 'block_dash'); + + $cssclassses = explode(',', get_config('block_dash', 'cssclass')); + $cssclassses = array_combine($cssclassses, $cssclassses); + $mform->addElement('autocomplete', 'config_css_class', get_string('cssclass', 'block_dash'), $cssclassses, $attributes); $mform->setType('config_css_class', PARAM_TEXT); + $mform->addHelpButton('config_css_class', 'cssclass', 'block_dash'); $mform->addElement('filemanager', 'config_backgroundimage', get_string('backgroundimage', 'block_dash'), null, ['subdirs' => 0, 'maxfiles' => 1, 'accepted_types' => ['image'], 'return_types' => FILE_INTERNAL | FILE_EXTERNAL]); $mform->addHelpButton('config_backgroundimage', 'backgroundimage', 'block_dash'); - $mform->addElement('text', 'config_backgroundgradient', get_string('backgroundgradient', 'block_dash'), + $postions = [ + 'initial' => get_string('initial', 'block_dash'), + 'left top' => get_string('lefttop', 'block_dash'), + 'left center' => get_string('leftcenter', 'block_dash'), + 'left bottom' => get_string('leftbottom', 'block_dash'), + 'right top' => get_string('righttop', 'block_dash'), + 'right center' => get_string('rightcenter', 'block_dash'), + 'right bottom' => get_string('rightbottom', 'block_dash'), + 'center top' => get_string('centertop', 'block_dash'), + 'center center' => get_string('centercenter', 'block_dash'), + 'center bottom' => get_string('centerbottom', 'block_dash'), + 'custom' => get_string('strcustom', 'block_dash'), + ]; + // Module background image poisiton. + $mform->addElement('select', 'config_backgroundimage_position', get_string('backgroundposition', 'block_dash'), + $postions); + $mform->setType('config_backgroundimage_position', PARAM_RAW); + $mform->addHelpButton('config_backgroundimage_position', 'backgroundposition', 'block_dash'); + + // Module background image custom poisiton. + $mform->addElement('text', 'config_backgroundimage_customposition', + get_string('designercustombgposition', 'block_dash')); + $mform->setType('config_backgroundimage_customposition', PARAM_RAW); + $mform->addHelpButton('config_backgroundimage_customposition', 'backgroundposition', 'block_dash'); + $mform->hideIf('config_backgroundimage_customposition', 'config_backgroundimage_position', 'neq', 'custom'); + + // Module background image size. + $sizes = [ + 'auto' => get_string('auto', 'block_dash'), + 'cover' => get_string('cover', 'block_dash'), + 'contain' => get_string('contain', 'block_dash'), + 'custom' => get_string('strcustom', 'block_dash'), + ]; + $mform->addElement('select', 'config_backgroundimage_size', get_string('backgroundsize', + 'block_dash'), $sizes); + $mform->setType('config_backgroundimage_size', PARAM_RAW); + $mform->addHelpButton('config_backgroundimage_size', 'backgroundsize', 'block_dash'); + + // Module background image custom size. + $mform->addElement('text', 'config_backgroundimage_customsize', get_string('designercustombgsize', 'block_dash')); + $mform->setType('config_backgroundimage_customsize', PARAM_RAW); + $mform->addHelpButton('config_backgroundimage_customsize', 'backgroundsize', 'block_dash'); + $mform->hideIf('config_backgroundimage_customsize', 'config_backgroundimage_size', 'neq', 'custom'); + + require_once($CFG->dirroot.'/blocks/dash/form/gradientpicker.php'); + MoodleQuickForm::registerElementType('dashgradientpicker', $CFG->dirroot.'/blocks/dash/form/gradientpicker.php', + 'moodlequickform_dashgradientpicker'); + + $mform->addElement('dashgradientpicker', 'config_backgroundgradient', get_string('backgroundgradient', 'block_dash'), ['placeholder' => 'linear-gradient(#e66465, #9198e5)']); $mform->setType('config_backgroundgradient', PARAM_TEXT); $mform->addHelpButton('config_backgroundgradient', 'backgroundgradient', 'block_dash'); - $mform->addElement('text', 'config_headerfootercolor', get_string('fontcolor', 'block_dash')); - $mform->setType('config_headerfootercolor', PARAM_TEXT); + require_once($CFG->dirroot.'/blocks/dash/form/element-colorpicker.php'); + MoodleQuickForm::registerElementType('dashcolorpicker', $CFG->dirroot.'/blocks/dash/form/element-colorpicker.php', + 'moodlequickform_dashcolorpicker'); + + $mform->addElement('dashcolorpicker', 'config_headerfootercolor', get_string('fontcolor', 'block_dash')); + $mform->setType('config_headerfootercolor', PARAM_RAW); $mform->addHelpButton('config_headerfootercolor', 'fontcolor', 'block_dash'); - $mform->addElement('text', 'config_css[border]', get_string('border', 'block_dash')); - $mform->setType('config_css[border]', PARAM_TEXT); - $mform->addHelpButton('config_css[border]', 'border', 'block_dash'); + $mform->addElement('select', 'config_border_option', get_string('border_option', 'block_dash'), [ + 0 => get_string('hidden', 'block_dash'), + 1 => get_string('visible'), + ]); + $mform->setType('config_border_option', PARAM_INT); + $mform->setDefault('config_border_option', 1); + $mform->addHelpButton('config_border_option', 'border_option', 'block_dash'); + + $mform->addElement('text', 'config_border', get_string('bordervalue', 'block_dash')); + $mform->setType('config_border', PARAM_TEXT); + $mform->addHelpButton('config_border', 'border', 'block_dash'); + $mform->hideIf('config_border', 'config_border_option', 'eq', 0); $mform->addElement('text', 'config_css[min-height]', get_string('minheight', 'block_dash')); $mform->setType('config_css[min-height]', PARAM_TEXT); @@ -139,6 +216,8 @@ public function add_datasource_group(&$mform, $config) { if (!isset($config->data_source_idnumber)) { self::dash_features_list($mform, $this->block->context, $this->page); + $mform->addElement('hidden', 'config_dash_configure_options', 1); + $mform->setType('config_dash_configure_options', PARAM_INT); } else { if ($ds = data_source_factory::build_data_source($config->data_source_idnumber, @@ -147,8 +226,8 @@ public function add_datasource_group(&$mform, $config) { } else { $label = get_string('datasourcemissing', 'block_dash'); } - $datalabel = ($ds->is_widget() - ? get_string('widget', 'block_dash') : get_string('datasource', 'block_dash')); + $datalabel = ($ds && $ds->is_widget()) + ? get_string('widget', 'block_dash') : get_string('datasource', 'block_dash'); $mform->addElement('static', 'data_source_label', $datalabel.' : ', $label); } @@ -167,20 +246,21 @@ public static function dash_features_list(&$mform, $context, $page) { // Group of datasources. if (has_capability('block/dash:managedatasource', $context)) { $datasources = data_source_factory::get_data_source_form_options(); - // Description of the datasources. $group[] = $mform->createElement('html', html_writer::tag('p', get_string('datasourcedesc', 'block_dash'), ['class' => 'dash-source-desc'])); $group[] = $mform->createElement('html', html_writer::start_div('datasource-content')); foreach ($datasources as $id => $source) { - $group[] = $mform->createElement('html', html_writer::start_div('datasource-item')); - $group[] = $mform->createElement('radio', 'config_data_source_idnumber', '', $source['name'], $id); - if ($help = $source['help']) { - $helpcontent = $OUTPUT->help_icon($help['name'], $help['component'], $help['name']); - $group[] = $mform->createElement('html', $helpcontent); + if (block_dash_visible_addons($id)) { + $group[] = $mform->createElement('html', html_writer::start_div('datasource-item')); + $group[] = $mform->createElement('radio', 'config_data_source_idnumber', '', $source['name'], $id); + if ($help = $source['help']) { + $helpcontent = $OUTPUT->help_icon($help['name'], $help['component'], $help['name']); + $group[] = $mform->createElement('html', $helpcontent); + } + $group[] = $mform->createElement('html', html_writer::end_div()); } - $group[] = $mform->createElement('html', html_writer::end_div()); } $group[] = $mform->createElement('html', html_writer::end_div()); $mform->addGroup($group, 'datasources', get_string('buildown', 'block_dash'), [' '], false); @@ -195,12 +275,15 @@ public static function dash_features_list(&$mform, $context, $page) { html_writer::tag('p', get_string('widgetsdesc', 'block_dash'), ['class' => 'dash-source-desc'])); $widgets[] = $mform->createElement('html', html_writer::start_div('datasource-content')); foreach ($widgetlist as $id => $source) { - $widgets[] = $mform->createElement('html', html_writer::start_div('datasource-item')); - $widgets[] = $mform->createElement('radio', 'config_data_source_idnumber', '', $source['name'], $id); - if ($source['help']) { - $widgets[] = $mform->createElement('html', $OUTPUT->help_icon($source['help'], 'block_dash', $source['help'])); + if (block_dash_visible_addons($id)) { + $widgets[] = $mform->createElement('html', html_writer::start_div('datasource-item')); + $widgets[] = $mform->createElement('radio', 'config_data_source_idnumber', '', $source['name'], $id); + if ($source['help']) { + $widgets[] = $mform->createElement('html', $OUTPUT->help_icon($source['help'], 'block_dash', + $source['help'])); + } + $widgets[] = $mform->createElement('html', html_writer::end_div()); } - $widgets[] = $mform->createElement('html', html_writer::end_div()); } $widgets[] = $mform->createElement('html', html_writer::end_div()); $mform->addGroup($widgets, 'widgets', get_string('readymatewidgets', 'block_dash'), [' '], false); diff --git a/form/element-colorpicker.php b/form/element-colorpicker.php new file mode 100644 index 0000000..448727c --- /dev/null +++ b/form/element-colorpicker.php @@ -0,0 +1,98 @@ +. + +/** + * Dash - Form element for color picker. + * + * @package block_dash + * @copyright 2021 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once('HTML/QuickForm/input.php'); +require_once($CFG->dirroot.'/lib/form/templatable_form_element.php'); +require_once($CFG->dirroot.'/lib/form/text.php'); + +/** + * Form element for color picker. + * + * @package block_dash + * @copyright 2021 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class moodlequickform_dashcolorpicker extends MoodleQuickForm_text implements templatable { + + use templatable_form_element { + export_for_template as export_for_template_base; + } + + /** + * Constructor. + * + * @param string $elementname (optional) Name of the text field. + * @param string $elementlabel (optional) Text field label. + * @param string $attributes (optional) Either a typical HTML attribute string or an associative array. + */ + public function __construct($elementname=null, $elementlabel=null, $attributes=null) { + parent::__construct($elementname, $elementlabel, $attributes); + $this->setType('text'); + + // Add a CSS class for styling the color picker. + $class = $this->getAttribute('class'); + if (empty($class)) { + $class = ''; + } + $this->updateAttributes(['class' => $class.' block_dash-form-colour-picker ']); + } + + /** + * Export for template. + * + * @param renderer_base $output + * @return array|stdClass + */ + public function export_for_template(renderer_base $output) { + global $PAGE; + + // Compose template context for the mform element. + $context = $this->export_for_template_base($output); + + // Build loading icon. + $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']); + $icondata = $icon->export_for_template($output); + $iconoutput = $output->render_from_template('core/pix_icon', $icondata); + + // Get ID of the element. + $id = $this->getAttribute('id'); + + // Add JS to append the color picker div before the element and initiate the color picker utility method. + $PAGE->requires->js_amd_inline(" + var element = document.getElementById('$id'); + var pickerDiv = document.createElement('div'); + pickerDiv.classList.add('admin_colourpicker', 'clearfix'); + pickerDiv.innerHTML = '$iconoutput'; // Add loading icon. + element.parentNode.prepend(pickerDiv); + element.parentNode.style.flexDirection = 'column'; + + // Init color picker utility. + M.util.init_colour_picker(Y, '$id'); + "); + + return $context; + } +} diff --git a/form/gradientpicker.php b/form/gradientpicker.php new file mode 100644 index 0000000..cc5690e --- /dev/null +++ b/form/gradientpicker.php @@ -0,0 +1,87 @@ +. + +/** + * Dash - Form element for color picker. + * + * @package block_dash + * @copyright 2021 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once('HTML/QuickForm/input.php'); +require_once($CFG->dirroot.'/lib/form/templatable_form_element.php'); +require_once($CFG->dirroot.'/lib/form/text.php'); + +/** + * Form element for color picker. + * + * @package block_dash + * @copyright 2021 bdecent gmbh + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class moodlequickform_dashgradientpicker extends MoodleQuickForm_text implements templatable { + + use templatable_form_element { + export_for_template as export_for_template_base; + } + + /** + * Constructor. + * + * @param string $elementname (optional) Name of the text field. + * @param string $elementlabel (optional) Text field label. + * @param string $attributes (optional) Either a typical HTML attribute string or an associative array. + */ + public function __construct($elementname=null, $elementlabel=null, $attributes=null) { + parent::__construct($elementname, $elementlabel, $attributes); + $this->setType('text'); + + // Add a CSS class for styling the color picker. + $class = $this->getAttribute('class'); + if (empty($class)) { + $class = ''; + } + $this->updateAttributes(['class' => $class.' block_dash-form-gradient-picker']); + } + + /** + * Export for template. + * + * @param renderer_base $output + * @return array|stdClass + */ + public function export_for_template(renderer_base $output) { + global $PAGE; + + // Compose template context for the mform element. + $context = $this->export_for_template_base($output); + + $PAGE->requires->js_amd_inline('require(["jquery", "block_dash/gradient"], function($) { + var background = document.querySelectorAll("input[name=config_backgroundgradient]")[0]; + if (background) { + new lc_color_picker(background, { + modes : ["linear-gradient", "solid"], + }); + } + })' + ); + + return $context; + } +} diff --git a/lang/en/block_dash.php b/lang/en/block_dash.php index 55aa198..db9e427 100644 --- a/lang/en/block_dash.php +++ b/lang/en/block_dash.php @@ -44,11 +44,14 @@ $string['blocktitle'] = 'Block title'; $string['blocktitle_help'] = 'Title displayed in block heading. Enter a short and descriptive title for what this block displays to the user.'; $string['blockwidth'] = 'Block width'; +$string['showheader'] = "Show header"; +$string['showheader_help'] = "Hide the block title if editing is turned off."; +$string['hidden'] = "Hidden"; $string['bodyfield'] = 'Body field'; $string['bootstrapversion'] = 'Theme bootstrap version'; $string['bootstrapversion_desc'] = 'Choose the version of Bootstrap your theme supports. Moodle is typically 4, and Totara is 3.'; $string['border'] = 'Border'; -$string['border_help'] = 'CSS border property value. Set to none for no borders.'; +$string['border_help'] = 'CSS border property value.'; $string['choosedatasource'] = 'Choose data source'; $string['columns'] = 'Columns'; $string['columns_help'] = 'The number of columns to display per row in the grid.'; @@ -214,6 +217,7 @@ $string['parseerror'] = 'Parse error. The content cannot be displayed.'; $string['permissions'] = 'Restrict access to'; $string['permissionscohort'] = 'Cohort'; +$string['permissionsrole'] = 'Role'; $string['permissionsloggedin'] = 'Must be logged in'; $string['permissionspublic'] = 'Public'; $string['perpage'] = 'Per page'; @@ -779,3 +783,98 @@ $string['programs:view'] = 'View programs'; $string['selfallocationwithkey'] = 'Sigup (Key required)'; $string['viewprogram'] = 'View program'; + +$string['createddate'] = 'Created date'; +$string['modifieddate'] = 'Last modified date'; +$string['duedate'] = 'Due date'; +$string['categoryurl'] = 'Category URL'; +$string['modulename'] = 'Module Name'; +$string['programbg'] = "Enrol Program image"; +$string['programbg_desc'] = ""; + +// Course certificate datasource strings. +$string['downloadcertificate'] = 'Download certificate'; +$string['certificatecodelinked'] = 'Code linked'; + +// Category datasource. +$string['categories'] = 'Categories'; +$string['categories_help'] = 'Categories datasource list the available categories.'; +$string['recentcoursename'] = 'Recent course'; +$string['categoryimagelink'] = 'Category link'; +$string['categoryimage'] = 'Category image'; +$string['categorycoursecount'] = 'Courses count'; +// ...Category image settings. +$string['categoryimgheading'] = 'Category image'; +$string['categoryimgheadingsub'] = 'Categories images'; +$string['categoryimgdesc'] = 'Add images for the categories'; +$string['categoryimgcategory'] = 'Image for category {$a->category}'; +$string['categoryimgfallback'] = 'Category fallback image'; +$string['categoryimgfallbackdesc'] = 'Upload the image for the default fallback of categories. If new categories are created, the fallback image will be displayed until a new image is added for the category'; +$string['categoryimageurl'] = 'Category image url'; + +$string['cssclass'] = "CSS classes"; +$string['cssclass_help'] = "Use a custom CSS class to apply multiple classes."; +$string['backgroundposition'] = 'Background Position'; +$string['backgroundposition_help'] = 'Background image will focused on the given position'; +$string['designercustombgposition'] = "Custom Background Position"; +$string['backgroundsize'] = 'Background Size'; +$string['backgroundsize_help'] = 'Background image will displayed in the size'; +$string['designercustombgsize'] = "Custom Background Size"; +$string['backgroundsize_help'] = 'Background image will displayed in the size'; + +$string['initial'] = "Initial"; +$string['lefttop'] = "Left Top"; +$string['leftcenter'] = "Left Center"; +$string['leftbottom'] = "Left Bottom"; +$string['righttop'] = "Right Top"; +$string['rightcenter'] = "Right Center"; +$string['rightbottom'] = "Right Bottom"; +$string['centertop'] = "Center Top"; +$string['centercenter'] = "Center Center"; +$string['centerbottom'] = "Center Bottom"; + +$string['auto'] = "Auto"; +$string['cover'] = "Cover"; +$string['contain'] = "Contain"; + +$string['strcustom'] = "Custom"; +$string['cohorts'] = "Cohorts"; +$string['users_mycohort'] = "Users in one of my cohorts"; + +$string['currentcategory'] = 'Current Category'; +$string['dashicon'] = 'Icon'; +$string['dashicon_help'] = 'This icon shows onlu in the dashboards data source.'; +$string['dashthumbnailimg'] = 'Thumbnail image'; +$string['dashthumbnailimgurl'] = 'Thumbnail image URL'; +$string['dashthumbnailimg_help'] = "This thumbnail image shows only in dashboards data source"; +$string['dashbgimg'] = 'Background image'; +$string['dashbgimg_help'] = 'This background image used as background for the dashboard.'; +$string['coredashboard'] = "Main dashboard"; +$string['backgroundimageurl'] = 'Background image URL'; + +$string['border_option'] = "Show border"; +$string['border_option_help'] = "You can add the border of dash block."; +$string['bordervalue'] = "Border Value"; + + +$string['managedashaddonplugins'] = "Manage addons"; +$string['dashaddonpluginname'] = 'Dash addon name'; +$string['hideshow'] = 'Hide/Show'; + +$string['maindashboard'] = "Main dashboard"; +$string['enterclasses'] = "Enter classes"; +// Permissions role context. +$string['permissionsrolecontext'] = 'Role Context'; +$string['permissionsrolecontext_help'] = 'Select the context for which the user\'s role should be checked (Any context or system context only)'; +// ...Dashaddon skill graph strings. +$string['managecapabilitymissing'] = 'Require capability "moodle/competency:competencymanage" to access the page is missing'; +$string['managecompentency'] = 'Manage competencies appearance'; +$string['totalprogress'] = 'Total progress'; +$string['setup'] = 'Setup'; +$string['compentenciesnotfound'] = 'Competency not found.'; +$string['competencyappearance'] = 'Competency appearances'; +$string['competencyheading'] = 'Setup competency appearance'; +$string['competencycolor'] = 'Competency color'; +$string['competencyimage'] = 'Competency image'; +$string['missingdatasource'] = "The datasources are missing. Please check them."; + diff --git a/lib.php b/lib.php index 992eabe..999c450 100644 --- a/lib.php +++ b/lib.php @@ -22,6 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use block_dash\local\data_source\categories_data_source; use block_dash\local\data_source\form\preferences_form; use block_dash\local\layout\grid_layout; use block_dash\local\layout\accordion_layout; @@ -175,13 +176,13 @@ function block_dash_output_fragment_block_preferences_form($args) { */ function block_dash_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=[]) { - if ($context->contextlevel != CONTEXT_BLOCK) { + if ($context->contextlevel != CONTEXT_BLOCK && $context->contextlevel != CONTEXT_SYSTEM) { return false; } require_login(); - if ($filearea == 'images') { + if ($filearea == 'images' || $filearea == 'categoryimg') { $relativepath = implode('/', $args); @@ -338,3 +339,34 @@ function block_dash_get_data_collection() { return version_compare(phpversion(), '8.1', '<') ? new block_dash\local\data_grid\data\data_collection() : new \block_dash\local\data_grid\data\data_collection_new(); } + +/** + * Return the dash addon visible staus. + * + * @param int $id ID + * @return bool + */ +function block_dash_visible_addons($id) { + global $CFG; + preg_match('/(dashaddon_[a-zA-Z_]+)/', $id, $matches); + if ($matches) { + $value = $matches[1]; + $parts = explode('\\', $value); + if ($parts) { + $addon = $parts[0]; + $addondependencies = $addon . "_extend_added_dependencies"; + if (get_config($addon, 'enabled')) { + $addonplugin = explode("dashaddon_", $addon)[1]; + if (file_exists($CFG->dirroot. "/local/dash/addon/$addonplugin/lib.php")) { + require_once($CFG->dirroot. "/local/dash/addon/$addonplugin/lib.php"); + if (function_exists($addondependencies) && !empty($addondependencies())) { + return false; + } + } + } else { + return false; + } + } + } + return true; +} diff --git a/settings.php b/settings.php index 0efb24a..c947c5e 100644 --- a/settings.php +++ b/settings.php @@ -39,6 +39,26 @@ ] )); + // Css classes. + $settings->add(new admin_setting_configtext( + 'block_dash/cssclass', + get_string('cssclass', 'block_dash'), + get_string('cssclass_help', 'block_dash'), + '', + PARAM_TEXT + )); + + $settings->add(new admin_setting_configselect( + 'block_dash/showheader', + get_string('showheader', 'block_dash'), + get_string('showheader_help', 'block_dash'), + 1, + [ + 0 => get_string('hidden', 'block_dash'), + 1 => get_string('visible'), + ] + )); + $settings->add(new admin_setting_configselect( 'block_dash/hide_when_empty', get_string('hidewhenempty', 'block_dash'), @@ -89,9 +109,39 @@ get_string('suggestusers_desc', 'block_dash'), [], $users) ); + if ($ADMIN->fulltree) {// Category images. + + $settings->add(new admin_setting_heading('block_dash_categoryimg', get_string('categoryimgheadingsub', 'block_dash'), + format_text(get_string('categoryimgdesc', 'block_dash'), FORMAT_MARKDOWN))); + + $name = 'block_dash/categoryimgfallback'; + $title = get_string('categoryimgfallback', 'block_dash'); + $description = get_string('categoryimgfallbackdesc', 'block_dash'); + $default = 'categoryimg'; + $setting = new admin_setting_configstoredfile($name, $title, $description, $default, 0); + $setting->set_updatedcallback('theme_reset_all_caches'); + $settings->add($setting); + + $coursecats = core_course_category::make_categories_list(); + + // Go through all categories and create the necessary settings. + foreach ($coursecats as $key => $value) { + // Category Icons for each category. + $name = 'block_dash/categoryimg'; + $title = $value; + $description = get_string('categoryimgcategory', 'block_dash', ['category' => $value]); + $filearea = 'categoryimg'; + $setting = new admin_setting_configstoredfile($name . $key, $title, $description, $default, $key); + $setting->set_updatedcallback('theme_reset_all_caches'); + $settings->add($setting); + } + unset($coursecats); + } + $PAGE->requires->js_amd_inline(" require(['core/form-autocomplete'], function(module) { module.enhance('#id_s_block_dash_suggestusers'); }); "); + } diff --git a/styles.css b/styles.css index 2065579..de87aa8 100644 --- a/styles.css +++ b/styles.css @@ -966,7 +966,6 @@ h5:before, .block_dash .card-body .form-inline .col-md-3.col-form-label p { font-weight: bold; } -.block_dash .card-body .form-inline .col-md-9.form-inline .custom-addon, .block_dash .card-body .form-inline .col-md-9.form-inline .datasource-content { width: 100%; display: flex; diff --git a/tests/assets/background.jpg b/tests/assets/background.jpg new file mode 100644 index 0000000..59d77b2 Binary files /dev/null and b/tests/assets/background.jpg differ diff --git a/tests/assets/img-1.jpg b/tests/assets/img-1.jpg new file mode 100644 index 0000000..fb0c241 Binary files /dev/null and b/tests/assets/img-1.jpg differ diff --git a/tests/assets/img-2.jpg b/tests/assets/img-2.jpg new file mode 100644 index 0000000..de79e1d Binary files /dev/null and b/tests/assets/img-2.jpg differ diff --git a/tests/assets/img-3.jpg b/tests/assets/img-3.jpg new file mode 100644 index 0000000..dfd8fb8 Binary files /dev/null and b/tests/assets/img-3.jpg differ diff --git a/tests/behat/behat_block_dash.php b/tests/behat/behat_block_dash.php index 1a23109..848be19 100644 --- a/tests/behat/behat_block_dash.php +++ b/tests/behat/behat_block_dash.php @@ -113,8 +113,27 @@ public function i_open_the_dash_block($blockname) { $this->execute("behat_blocks::i_open_the_blocks_action_menu", $this->escape($blockname)); $this->execute('behat_general::i_click_on_in_the', - array("Preference", "link", $this->escape($blockname), "block") + ["Preference", "link", $this->escape($blockname), "block"] ); } + /** + * Check that the focus mode enable. + * + * @Given /^I check dash css "(?P(?:[^"]|\\")*)" "(?P(?:[^"]|\\")*)" "(?P(?:[^"]|\\")*)"$/ + * @param string $value + * @param string $selector + * @param string $type + * @throws ExpectationException + */ + public function i_check_dash_css($value, $selector, $type): void { + $stylejs = " + return ( + Y.one('{$selector}').getComputedStyle('{$type}') + ) + "; + if (strpos($this->evaluate_script($stylejs), $value) === false) { + throw new ExpectationException("Doesn't working correct style", $this->getSession()); + } + } } diff --git a/tests/behat/dash.feature b/tests/behat/dash.feature new file mode 100644 index 0000000..6bb21b6 --- /dev/null +++ b/tests/behat/dash.feature @@ -0,0 +1,184 @@ +@block @block_dash @dash_feature @javascript @_file_upload +Feature: Add a dash to an admin pages + In order to check the dash featuers + I can add the dash block to the dashboard + + Background: + Given the following "categories" exist: + | name | category | idnumber | + | Category 01 | 0 | CAT1 | + | Category 02 | 0 | CAT2 | + And the following "courses" exist: + | fullname | shortname | category | enablecompletion | + | Course 1 | C1 | CAT1 | 1 | + | Course 2 | C2 | CAT1 | 0 | + | Course 3 | C3 | CAT2 | 1 | + | Course 4 | C4 | CAT2 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | First | student1@example.com | + | teacher1 | Teacher | First | teacher1@example.com | + | student2 | Student | Two | student2@example.com | + And the following "activities" exist: + | activity | course | idnumber | section | name | intro | completion | completionview | + | page | C1 | page1 | 0 | Test page name | Test page description | 2 | 1 | + | page | C1 | page2 | 1 | Test page name 2 | Test page description | 2 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student1 | C2 | student | + | teacher1 | C1 | teacher | + | teacher1 | C2 | teacher | + + Scenario: Global Settings : Show header feature + And I log in as "admin" + And I navigate to "Plugins > Blocks > Dash" in site administration + Then I set the field "Show header" to "Hidden" + Then I press "Save changes" + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn editing mode on + And I create dash "Users" datasource + Then I configure the "New Dash" block + And I set the field "Block title" to "Datasource: Users" + And I set the following fields to these values: + | Region | content | + And I press "Save changes" + Then I should see "Datasource: Users" + Then I turn editing mode off + Then I should not see "Datasource: Users" + And I click on "Reset Dashboard for all users" "button" + Then I log in as "student1" + Then I follow "Dashboard" + Then I turn editing mode on + Then I should see "Datasource: Users" + Then I turn editing mode off + Then I should not see "Datasource: Users" + Then I log in as "admin" + And I navigate to "Plugins > Blocks > Dash" in site administration + Then I set the field "Show header" to "Visible" + Then I press "Save changes" + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn editing mode on + And I create dash "Users" datasource + Then I configure the "New Dash" block + And I set the field "Block title" to "Datasource: Users Report" + And I set the following fields to these values: + | Region | content | + And I press "Save changes" + Then I should see "Datasource: Users Report" + Then I turn editing mode off + Then I should see "Datasource: Users Report" + And I click on "Reset Dashboard for all users" "button" + Then I log in as "student1" + Then I follow "Dashboard" + Then I turn editing mode on + Then I should see "Datasource: Users Report" + Then I turn editing mode off + Then I should see "Datasource: Users Report" + + Scenario: Block Settings : Show header feature + And I log in as "admin" + And I navigate to "Plugins > Blocks > Dash" in site administration + Then I set the field "Show header" to "Hidden" + Then I press "Save changes" + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn editing mode on + And I create dash "Users" datasource + Then I configure the "New Dash" block + And I set the field "Block title" to "Datasource: Users" + And I set the following fields to these values: + | Region | content | + | Show header | Hidden | + And I press "Save changes" + Then I should see "Datasource: Users" + Then I turn editing mode off + Then I should not see "Datasource: Users" + And I click on "Reset Dashboard for all users" "button" + Then I log in as "student1" + Then I follow "Dashboard" + Then I turn editing mode on + Then I should see "Datasource: Users" + Then I turn editing mode off + Then I should not see "Datasource: Users" + + Scenario: Block Settings: Dash settings improvements + And I log in as "admin" + #General setting css classes + And I navigate to "Plugins > Blocks > Dash" in site administration + And I set the following fields to these values: + | CSS classes | dash-card-block | + And I press "Save changes" + + # Dash block setting css classes + And I follow dashboard + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn dash block editing mode on + And I add the "Dash" block + And I configure the "New Dash" block + And I expand all fieldsets + And I set the following fields to these values: + | CSS classes | dash-element, dash-card | + And I press "Save changes" + And I click on "Reset Dashboard for all users" "button" + And I follow dashboard + And ".dash-element.dash-card" "css_element" should exist in the ".block-region .block_dash" "css_element" + + # Gradient color + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn dash block editing mode on + And I add the "Dash" block + And I configure the "New Dash" block + And I expand all fieldsets + And I set the following fields to these values: + | Background gradient | linear-gradient(90deg, rgba(255, 210, 0, .2) 0%, rgba(70, 210, 251, .2) 100%) | + And I press "Save changes" + And I click on "Reset Dashboard for all users" "button" + And I follow dashboard + And I check dash css "linear-gradient(90deg, rgba(255, 210, 0, 0.2) 0%, rgba(70, 210, 251, 0.2) 100%)" "section.block_dash:nth-of-type(2)" "background-image" + + # Font color picker + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn dash block editing mode on + And I add the "Dash" block + And I click on "Users" "radio" + And I configure the "New Dash" block + And I expand all fieldsets + And I set the following fields to these values: + | Block title | Users 01| + | Font color | #c60061 | + And I press "Save changes" + And I click on "Reset Dashboard for all users" "button" + And I follow dashboard + And I check dash css "rgb(198, 0, 97)" "section.block_dash:nth-of-type(3) .card-title" "color" + + # Border color + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn dash block editing mode on + And I add the "Dash" block + And I configure the "New Dash" block + And I expand all fieldsets + And I set the following fields to these values: + | Block title | Border settings | + | Border | Visible | + And I press "Save changes" + And I click on "Reset Dashboard for all users" "button" + And I follow dashboard + And I check dash css "1px solid rgba(0, 0, 0, 0.125)" "section.block_dash:nth-of-type(4)" "border" + + Scenario: Default fields after selecting the data source + And I log in as "admin" + # Users data source + And I navigate to "Appearance > Default Dashboard page" in site administration + And I turn dash block editing mode on + And I add the "Dash" block + And I click on "Users" "radio" + And I configure the "New Dash" block + And I expand all fieldsets + And I set the following fields to these values: + | Block title | Users | + And I press "Save changes" + And I click on "Reset Dashboard for all users" "button" + And I follow dashboard + And I should see "Student" + And I should see "First" + And I should see "student1@example.com" diff --git a/tests/behat/mygroups.feature b/tests/behat/mygroups.feature index 3893209..8f8bb11 100644 --- a/tests/behat/mygroups.feature +++ b/tests/behat/mygroups.feature @@ -6,64 +6,65 @@ Feature: Add My Groups widget in dash block Background: Given the following "categories" exist: - | name | category | idnumber | - | Category 1 | 0 | CAT1 | - | Category 2 | 0 | CAT2 | - | Category 3 | CAT2 | CAT3 | + | name | category | idnumber | + | Category 1 | 0 | CAT1 | + | Category 2 | 0 | CAT2 | + | Category 3 | CAT2 | CAT3 | And the following "courses" exist: - | fullname | shortname | category | enablecompletion| - | Course 1 | C1 | 0 | 1 | - | Course 2 | C2 | CAT1 | 0 | - | Course 3 | C3 | CAT2 | 1 | - | Course 4 | C4 | CAT3 | 1 | + | fullname | shortname | category | enablecompletion | + | Course 1 | C1 | 0 | 1 | + | Course 2 | C2 | CAT1 | 0 | + | Course 3 | C3 | CAT2 | 1 | + | Course 4 | C4 | CAT3 | 1 | And the following "users" exist: | username | firstname | lastname | email | | student1 | Student | First | student1@example.com | - | student2 | Student | Two | student2@example.com | - | student3 | Student | Three | student3@example.com | - | student4 | Student | Four | student4@example.com | - | student5 | Student | Five | student5@example.com | + | student2 | Student | Two | student2@example.com | + | student3 | Student | Three | student3@example.com | + | student4 | Student | Four | student4@example.com | + | student5 | Student | Five | student5@example.com | | teacher1 | Teacher | First | teacher1@example.com | - | manager | Max | Manager | man@example.com | + | manager | Max | Manager | man@example.com | And the following "role assigns" exist: - | user | role | contextlevel | reference | - | manager | manager | System | | + | user | role | contextlevel | reference | + | manager | manager | System | | And the following "course enrolments" exist: - | user | course | role | - | manager | C1 | manager | - | manager | C2 | manager | - | manager | C3 | manager | - | manager | C4 | manager | - | student1 | C1 | student | - | student2 | C1 | student | - | student1 | C2 | student | - | student2 | C2 | student | - | student1 | C3 | student | - | student2 | C3 | student | - | student3 | C2 | student | + | user | course | role | + | manager | C1 | manager | + | manager | C2 | manager | + | manager | C3 | manager | + | manager | C4 | manager | + | student1 | C1 | student | + | student2 | C1 | student | + | student1 | C2 | student | + | student2 | C2 | student | + | student1 | C3 | student | + | student2 | C3 | student | + | student3 | C2 | student | And the following "groups" exist: - | name | course | idnumber | enablemessaging | + | name | course | idnumber | enablemessaging | | Group C1 1 | C1 | G1 | 1 | | Group C1 2 | C1 | G2 | 1 | | Group C2 1 | C2 | G3 | 1 | And the following "group members" exist: | user | group | - | student1 | G1 | - | student2 | G1 | - | student1 | G2 | - | student1 | G3 | - | manager | G1 | - | manager | G2 | + | student1 | G1 | + | student2 | G1 | + | student1 | G2 | + | student1 | G3 | + | manager | G1 | + | manager | G2 | And I log in as "admin" And I navigate to "Appearance > Default Dashboard page" in site administration And I turn dash block editing mode on And I add the "Dash" block + And I click on "My groups" "radio" And I configure the "New Dash" block - And I click on "#id_config_data_source_idnumber_block_dashlocalwidgetgroupsgroups_widget" "css_element" And I set the following fields to these values: - | Region | content | + | Block title | My groups | + | Region | content | And I press "Save changes" And I click on "Reset Dashboard for all users" "button" And I log out @@ -71,8 +72,8 @@ Feature: Add My Groups widget in dash block @javascript Scenario: User groups widget in Dash Block Given I log in as "student1" - And I should see "Group C1 1" in the "Dash" "block" - And I should see "Group C2 1" in the "Dash" "block" + And I should see "Group C1 1" in the "My groups" "block" + And I should see "Group C2 1" in the "My groups" "block" Then the "title" attribute of ".block_dash-community-block .list-block:nth-child(1) img" "css_element" should contain "Max Manager" And I click on ".dropdown-toggle" "css_element" in the ".block_dash-community-block .list-block:nth-child(1)" "css_element" And I click on ".group-widget-viewmembers" "css_element" in the ".block_dash-community-block .list-block:nth-child(1)" "css_element" @@ -85,21 +86,21 @@ Feature: Add My Groups widget in dash block @javascript Scenario: Leave group using dash block Given I log in as "student1" - And I should see "Group C2 1" in the "Dash" "block" + And I should see "Group C2 1" in the "My groups" "block" And I click on ".dropdown-toggle" "css_element" in the ".block_dash-community-block .list-block:nth-child(3)" "css_element" And I click on ".group-widget-leavegroup" "css_element" in the ".block_dash-community-block .list-block:nth-child(3)" "css_element" And I should see "Do you really want to leave the group Group C2 1" in the ".modal-body" "css_element" And I click on "Confirm" "button" - And I should not see "Group C2 1" in the "Dash" "block" + And I should not see "Group C2 1" in the "My groups" "block" @javascript Scenario: Add User to existing group using dash block Given I log in as "manager" - And I should see "Group C1 2" in the "Dash" "block" + And I should see "Group C1 2" in the "My groups" "block" And I click on ".dropdown-toggle" "css_element" in the ".block_dash-community-block .list-block:nth-child(2)" "css_element" And I click on ".add-group-users" "css_element" in the ".block_dash-community-block .list-block:nth-child(2)" "css_element" And I set the following fields to these values: - | User | Student Two | + | User | Student Two | And I click on "Save changes" "button" in the ".modal-footer" "css_element" Then the "title" attribute of ".block_dash-community-block .list-block:nth-child(2) .img-block:nth-child(2) img" "css_element" should contain "Student Two" And I click on ".dropdown-toggle" "css_element" in the ".block_dash-community-block .list-block:nth-child(2)" "css_element" @@ -109,12 +110,12 @@ Feature: Add My Groups widget in dash block @javascript Scenario: Create a new group using dash block Given I log in as "manager" - And I should not see "Group C4 1" in the "Dash" "block" + And I should not see "Group C4 1" in the "My groups" "block" And I click on ".dropdown-toggle" "css_element" in the ".edit-block" "css_element" And I click on ".create-group" "css_element" in the ".edit-block" "css_element" And I set the following fields to these values: - | Group name | Group C4 1 | + | Group name | Group C4 1 | And I open the autocomplete suggestions list And I click on "Course 4" item in the autocomplete list And I click on "Save changes" "button" in the ".modal-footer" "css_element" - And I should see "Group C4 1" in the "Dash" "block" + And I should see "Group C4 1" in the "My groups" "block" diff --git a/tests/behat/mylearning.feature b/tests/behat/mylearning.feature index 1547bd4..7405624 100644 --- a/tests/behat/mylearning.feature +++ b/tests/behat/mylearning.feature @@ -6,16 +6,16 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con Background: Given the following "categories" exist: - | name | category | idnumber | - | Category 1 | 0 | CAT1 | - | Category 2 | 0 | CAT2 | - | Category 3 | CAT2 | CAT3 | + | name | category | idnumber | + | Category 1 | 0 | CAT1 | + | Category 2 | 0 | CAT2 | + | Category 3 | CAT2 | CAT3 | And the following "courses" exist: - | fullname | shortname | category | enablecompletion| - | Course 1 | C1 | 0 | 1 | - | Course 2 | C2 | CAT1 | 0 | - | Course 3 | C3 | CAT2 | 1 | - | Course 4 | C4 | CAT3 | 1 | + | fullname | shortname | category | enablecompletion | + | Course 1 | C1 | 0 | 1 | + | Course 2 | C2 | CAT1 | 0 | + | Course 3 | C3 | CAT2 | 1 | + | Course 4 | C4 | CAT3 | 1 | And the following "users" exist: | username | firstname | lastname | email | | student1 | Student | First | student1@example.com | @@ -23,26 +23,27 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con | student2 | Student | Two | student2@example.com | And the following "activities" exist: - | activity | course | idnumber |section | name | intro | completion | completionview | - | page | C1 | page1 | 0 | Test page name | Test page description | 2 | 1 | - | page | C1 | page2 | 1 | Test page name 2 | Test page description | 2 | 1 | + | activity | course | idnumber | section | name | intro | completion | completionview | + | page | C1 | page1 | 0 | Test page name | Test page description | 2 | 1 | + | page | C1 | page2 | 1 | Test page name 2 | Test page description | 2 | 1 | And the following "course enrolments" exist: - | user | course | role | - | student1 | C1 | student | - | teacher1 | C1 | teacher | - | student1 | C2 | student | - | student1 | C3 | student | + | user | course | role | + | student1 | C1 | student | + | teacher1 | C1 | teacher | + | student1 | C2 | student | + | student1 | C3 | student | And I log in as "admin" And I navigate to "Appearance > Default Dashboard page" in site administration And I turn dash block editing mode on And I add the "Dash" block + And I click on "My learning" "radio" And I configure the "New Dash" block - And I click on "#id_config_data_source_idnumber_block_dashlocalwidgetmylearningmylearning_widget" "css_element" And I set the following fields to these values: - | Region | content | - | Content | My learaning empty state content| + | Block title | My Learning | + | Region | content | + | Content | My learaning empty state content | And I press "Save changes" And I click on "Reset Dashboard for all users" "button" And I log out @@ -50,10 +51,10 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con @javascript Scenario: Add the dash mylearning widget block on the dashboard Given I log in as "student1" - Then I should see "Course 1" in the "Dash" "block" - And I should see "Course 2" in the "Dash" "block" - And I should see "Course 3" in the "Dash" "block" - And I should not see "Course 4" in the "Dash" "block" + Then I should see "Course 1" in the "My Learning" "block" + And I should see "Course 2" in the "My Learning" "block" + And I should see "Course 3" in the "My Learning" "block" + And I should not see "Course 4" in the "My Learning" "block" @javascript Scenario: Course completion status in mylearning widget @@ -67,9 +68,9 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con And I press "Save changes" And I log out When I log in as "student1" - Then I should see "Course 1" in the "Dash" "block" + Then I should see "Course 1" in the "My Learning" "block" And I should see "0" in the ".card-header:nth-child(1)" "css_element" - And I click on "General" "button" in the "Dash" "block" + And I click on "General" "button" in the "My Learning" "block" Then I click on "Test page name" "link" And I follow dashboard Then the "class" attribute of ".block_dash-info-element .card:nth-child(1)" "css_element" should contain "completed-bg" @@ -81,7 +82,7 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con And I am on "Course 1" course homepage And I navigate to "Badges > Add a new badge" in current page administration And I set the following fields to these values: - | id_name | Badge 1 | + | id_name | Badge 1 | | id_description | Badge 1 | And I upload "blocks/badges/tests/fixtures/badge.png" file to "Image" filemanager And I press "Create badge" @@ -96,13 +97,13 @@ Feature: Enable the widget in dash block on the dashboard page and view it's con And I press "Award badge" And I log out When I log in as "student1" - Then ".collected .activatebadge[alt=\"Badge 1\"]" "css_element" should exist in the "Dash" "block" + Then ".collected .activatebadge[alt=\"Badge 1\"]" "css_element" should exist in the "My Learning" "block" @javascript Scenario: Check the empty state option. Given I log in as "student2" - Then I should see "My learaning empty state content" in the "Dash" "block" + Then I should see "My learaning empty state content" in the "My Learning" "block" And I log out When I log in as "student1" - Then I should not see "My learaning empty state content" in the "Dash" "block" + Then I should not see "My learaning empty state content" in the "My Learning" "block" And I log out diff --git a/tests/widgets_test.php b/tests/widgets_test.php index 793c648..0b0728c 100644 --- a/tests/widgets_test.php +++ b/tests/widgets_test.php @@ -32,9 +32,46 @@ * @group block_dash * @group bdecent * @group widgets_test + * @runInSeparateProcess + * @runTestsInSeparateProcesses */ class widgets_test extends \advanced_testcase { + /** + * Demo of test user. + * + * @var array + */ + protected $user; + + /** + * Demo Course 1 + * + * @var array + */ + protected $course1; + + /** + * Demo Course 2 + * + * @var array + */ + protected $course2; + + /** + * Demo Course 3 + * + * @var array + */ + protected $course3; + + /** + * List of test users. + * + * @var array + */ + protected $users; + /** * This method is called before each test. */ @@ -117,6 +154,9 @@ protected function create_block($page) { * * @covers ::contacts_widget * @return void + * + * @runInSeparateProcess + * @runTestsInSeparateProcesses */ public function test_mylearning() { $user = self::getDataGenerator()->create_and_enrol($this->course1, 'student'); diff --git a/thirdpartylibs.xml b/thirdpartylibs.xml index a192fec..5a5eb3d 100644 --- a/thirdpartylibs.xml +++ b/thirdpartylibs.xml @@ -28,4 +28,11 @@ Apache 2.0 + + amd/src/lc_color_picker.js + Color picker + 2.0 + Apache + 2.0 + diff --git a/version.php b/version.php index bbe4744..1d1ef38 100644 --- a/version.php +++ b/version.php @@ -18,15 +18,15 @@ * Version details * * @package block_dash - * @copyright 2020 onwards bdecent gmbh + * @copyright 2022 bdecent gmbh * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024031800; -$plugin->requires = 2022112800; // Requires Moodle 4.1. -$plugin->component = 'block_dash'; -$plugin->maturity = MATURITY_STABLE; -$plugin->release = '1.9'; +$plugin->version = 2024050802; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2021051700; // Requires this Moodle version. +$plugin->component = 'block_dash'; // Full name of the plugin (used for diagnostics). +$plugin->maturity = MATURITY_RC; +$plugin->release = '2.0 RC'; $plugin->supported = [401, 403];