From 6a1e0c591a1cd3a3b97de9fc00a09cf06827113e Mon Sep 17 00:00:00 2001 From: Shana Moore Date: Mon, 11 Sep 2023 15:49:05 -0700 Subject: [PATCH 01/20] =?UTF-8?q?=F0=9F=8E=81=20Contributions=20from=20PAL?= =?UTF-8?q?NI/PALCI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Which were resolved via the following PRs: - https://github.com/scientist-softserv/palni-palci/pull/768 - https://github.com/scientist-softserv/palni-palci/pull/786 - https://github.com/scientist-softserv/palni-palci/pull/790 - https://github.com/scientist-softserv/palni-palci/pull/807 - https://github.com/scientist-softserv/palni-palci/pull/850 - https://github.com/scientist-softserv/palni-palci/pull/781 - https://github.com/scientist-softserv/palni-palci/pull/787 - https://github.com/scientist-softserv/palni-palci/pull/795 - https://github.com/scientist-softserv/palni-palci/pull/788 - https://github.com/scientist-softserv/palni-palci/pull/793 - https://github.com/scientist-softserv/palni-palci/pull/782 - https://github.com/scientist-softserv/palni-palci/pull/798 - https://github.com/scientist-softserv/palni-palci/pull/866 - https://github.com/scientist-softserv/palni-palci/pull/846 - https://github.com/scientist-softserv/palni-palci/pull/849 - https://github.com/scientist-softserv/palni-palci/pull/868 - https://github.com/scientist-softserv/palni-palci/pull/875 - https://github.com/scientist-softserv/palni-palci/pull/884 - https://github.com/scientist-softserv/palni-palci/pull/876 - https://github.com/scientist-softserv/palni-palci/pull/952 Contribute back the following features: - https://github.com/scientist-softserv/palni-palci/issues/125 - https://github.com/scientist-softserv/palni-palci/issues/740 - https://github.com/scientist-softserv/palni-palci/issues/742 - https://github.com/scientist-softserv/palni-palci/issues/746 - https://github.com/scientist-softserv/palni-palci/issues/772 - https://github.com/scientist-softserv/palni-palci/issues/773 - https://github.com/scientist-softserv/palni-palci/issues/774 - https://github.com/scientist-softserv/palni-palci/issues/776 - https://github.com/scientist-softserv/palni-palci/issues/778 - https://github.com/scientist-softserv/palni-palci/issues/839 - https://github.com/scientist-softserv/palni-palci/issues/840 - https://github.com/scientist-softserv/palni-palci/issues/864 Co-authored-by: Shana Moore Co-authored-by: Kirk Wang --- Gemfile | 1 + Gemfile.lock | 2 + app/assets/javascripts/application.js | 2 + app/assets/javascripts/cropper.min.js | 10 + app/assets/javascripts/fabric.min.js | 1 + app/assets/stylesheets/application.css | 1 + app/assets/stylesheets/cropper.min.css | 9 + app/assets/stylesheets/hyku.scss | 132 ++++++ app/assets/stylesheets/hyrax.scss | 6 + app/controllers/catalog_controller.rb | 7 +- .../strategies_controller_decorator.rb | 16 + .../hyrax/admin/appearances_controller.rb | 5 +- app/controllers/hyrax/homepage_controller.rb | 33 +- app/forms/hyrax/forms/admin/appearance.rb | 12 + app/forms/hyrax/generic_work_form.rb | 4 +- app/forms/hyrax/image_form.rb | 4 +- app/forms/hyrax/pdf_form_behavior.rb | 18 + app/helpers/application_helper.rb | 21 +- .../blacklight/advanced_search_helper.rb | 50 ++ app/helpers/features_helper.rb | 21 + app/helpers/hyku_helper.rb | 6 + app/helpers/iiif_print_helper.rb | 5 + app/helpers/pdf_js_helper.rb | 32 ++ app/helpers/shared_search_helper.rb | 5 +- app/indexers/app_indexer.rb | 21 +- app/indexers/generic_work_indexer.rb | 6 +- .../hyrax/file_set_indexer_decorator.rb | 39 ++ app/jobs/reindex_works_job.rb | 12 +- app/models/concerns/pdf_behavior.rb | 40 ++ app/models/generic_work.rb | 5 +- app/models/image.rb | 5 +- app/models/solr_document.rb | 20 + app/presenters/hyku/work_show_presenter.rb | 70 ++- .../hyrax/generic_work_presenter.rb | 1 + app/services/iiif_print/tenant_config.rb | 202 ++++++++ .../advanced/_advanced_search_fields.html.erb | 35 ++ .../_advanced_search_fields_qa.html.erb | 4 + .../appearances/_banner_image_form.html.erb | 78 ++- app/views/hyrax/admin/features/index.html.erb | 76 +++ .../hyrax/base/_analytics_button.html.erb | 5 + app/views/hyrax/base/_download_pdf.html.erb | 14 + app/views/hyrax/base/_form_files.html.erb | 69 +++ app/views/hyrax/base/_pdf_js.erb | 7 + app/views/hyrax/base/_relationships.html.erb | 39 ++ .../_relationships_parent_works_rows.html.erb | 12 + .../hyrax/base/_representative_media.html.erb | 12 + app/views/hyrax/base/_show_actions.html.erb | 5 - .../hyrax/base/_show_pdf_download_button.erb | 4 + .../hyrax/base/_show_pdf_viewer.html.erb | 4 + app/views/hyrax/base/show.html.erb | 9 +- app/views/hyrax/file_sets/_actions.html.erb | 58 +++ .../file_sets/media_display/_pdf.html.erb | 17 + .../records/edit_fields/_default.html.erb | 19 + app/views/shared/_appearance_styles.html.erb | 6 + .../layouts/homepage.html.erb | 3 +- .../hyrax/base/_relationships.html.erb | 12 + .../cultural_show/hyrax/base/show.html.erb | 30 +- .../image_show/hyrax/base/show.html.erb | 65 +++ .../homepage/_resource_type_stats.html.erb | 12 +- .../hyrax/homepage/all_collections.html.erb | 4 +- .../layouts/homepage.html.erb | 2 +- .../scholarly_show/hyrax/base/show.html.erb | 22 +- config/application.rb | 17 + config/features.rb | 10 + config/locales/de.yml | 2 + config/locales/devise_invitable.es.yml | 2 +- config/locales/en.yml | 6 +- config/locales/es.yml | 2 + config/locales/fr.yml | 2 + config/locales/hyrax.de.yml | 4 +- config/locales/hyrax.en.yml | 4 +- config/locales/hyrax.es.yml | 4 +- config/locales/hyrax.fr.yml | 4 +- config/locales/hyrax.it.yml | 4 +- config/locales/hyrax.pt-BR.yml | 4 +- config/locales/hyrax.zh.yml | 4 +- config/locales/it.yml | 2 + config/locales/pt-BR.yml | 2 + config/locales/simple_form.de.yml | 74 ++- config/locales/simple_form.es.yml | 39 +- config/locales/simple_form.fr.yml | 48 +- config/locales/simple_form.it.yml | 74 +-- config/locales/simple_form.pt-BR.yml | 46 +- config/locales/simple_form.zh.yml | 50 +- config/locales/zh.yml | 2 + public/pdf.js/viewer.html | 443 ++++++++++++++++++ spec/features/create_work_spec.rb | 20 +- spec/features/feature_flag_spec.rb | 20 +- spec/fixtures/pdf/archive.pdf | Bin 0 -> 324315 bytes spec/helpers/application_helper_spec.rb | 13 + .../blacklight/advanced_search_helper_spec.rb | 78 +++ spec/helpers/hyku_helper_spec.rb | 11 + spec/helpers/shared_search_helper_spec.rb | 40 +- .../hyrax/file_set_indexer_decorator_spec.rb | 25 + spec/models/solr_document_spec.rb | 44 ++ .../hyku/work_show_presenter_spec.rb | 74 ++- .../services/iiif_print/tenant_config_spec.rb | 208 ++++++++ .../base/_relationships.html.erb_spec.rb | 43 ++ 98 files changed, 2635 insertions(+), 237 deletions(-) create mode 100644 app/assets/javascripts/cropper.min.js create mode 100644 app/assets/javascripts/fabric.min.js create mode 100644 app/assets/stylesheets/cropper.min.css create mode 100644 app/controllers/flipflop/strategies_controller_decorator.rb create mode 100644 app/forms/hyrax/pdf_form_behavior.rb create mode 100644 app/helpers/blacklight/advanced_search_helper.rb create mode 100644 app/helpers/features_helper.rb create mode 100644 app/helpers/iiif_print_helper.rb create mode 100644 app/helpers/pdf_js_helper.rb create mode 100644 app/indexers/hyrax/file_set_indexer_decorator.rb create mode 100644 app/models/concerns/pdf_behavior.rb create mode 100644 app/services/iiif_print/tenant_config.rb create mode 100644 app/views/advanced/_advanced_search_fields.html.erb create mode 100644 app/views/advanced/_advanced_search_fields_qa.html.erb create mode 100644 app/views/hyrax/admin/features/index.html.erb create mode 100644 app/views/hyrax/base/_analytics_button.html.erb create mode 100644 app/views/hyrax/base/_download_pdf.html.erb create mode 100644 app/views/hyrax/base/_form_files.html.erb create mode 100644 app/views/hyrax/base/_pdf_js.erb create mode 100644 app/views/hyrax/base/_relationships.html.erb create mode 100644 app/views/hyrax/base/_relationships_parent_works_rows.html.erb create mode 100644 app/views/hyrax/base/_representative_media.html.erb create mode 100644 app/views/hyrax/base/_show_pdf_download_button.erb create mode 100644 app/views/hyrax/base/_show_pdf_viewer.html.erb create mode 100644 app/views/hyrax/file_sets/_actions.html.erb create mode 100644 app/views/hyrax/file_sets/media_display/_pdf.html.erb create mode 100644 app/views/records/edit_fields/_default.html.erb create mode 100644 app/views/themes/image_show/hyrax/base/show.html.erb create mode 100644 public/pdf.js/viewer.html create mode 100644 spec/fixtures/pdf/archive.pdf create mode 100644 spec/helpers/application_helper_spec.rb create mode 100644 spec/helpers/blacklight/advanced_search_helper_spec.rb create mode 100644 spec/helpers/hyku_helper_spec.rb create mode 100644 spec/indexers/hyrax/file_set_indexer_decorator_spec.rb create mode 100644 spec/models/solr_document_spec.rb create mode 100644 spec/services/iiif_print/tenant_config_spec.rb create mode 100644 spec/views/hyrax/base/_relationships.html.erb_spec.rb diff --git a/Gemfile b/Gemfile index 3fd4a6453..9be52ea91 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,7 @@ gem 'puma', '~> 5.6' # Use Puma as the app server gem 'rack-test', '0.7.0', group: %i[test] # rack-test >= 0.71 does not work with older Capybara versions (< 2.17). See #214 for more details gem 'rails-controller-testing', group: %i[test] gem 'rdf', '~> 3.1.15' # rdf 3.2.0 removed SerializedTransaction which ldp requires +gem 'redcarpet' # for Markdown constant gem 'redlock', '>= 0.1.2', '< 2.0' # lock redlock per https://github.com/samvera/hyrax/pull/5961 gem 'riiif', '~> 1.1' gem 'rolify' diff --git a/Gemfile.lock b/Gemfile.lock index 651139ee2..b14f534bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1019,6 +1019,7 @@ GEM rdf-xsd (3.1.1) rdf (~> 3.1) rexml (~> 3.2) + redcarpet (3.6.0) redic (1.5.3) hiredis redis (4.8.1) @@ -1351,6 +1352,7 @@ DEPENDENCIES rails (~> 5.2.5) rails-controller-testing rdf (~> 3.1.15) + redcarpet redlock (>= 0.1.2, < 2.0) riiif (~> 1.1) rolify diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index df9e83c18..c7c8860d1 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -16,6 +16,8 @@ //= require jquery3 //= require jquery_ujs //= require jquery.fontselect +//= require cropper.min + //= require dataTables/jquery.dataTables //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap //= require stat_slider diff --git a/app/assets/javascripts/cropper.min.js b/app/assets/javascripts/cropper.min.js new file mode 100644 index 000000000..959d89fe8 --- /dev/null +++ b/app/assets/javascripts/cropper.min.js @@ -0,0 +1,10 @@ +/*! + * Cropper.js v1.6.1 + * https://fengyuanchen.github.io/cropperjs + * + * Copyright 2015-present Chen Fengyuan + * Released under the MIT license + * + * Date: 2023-09-17T03:44:19.860Z + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Cropper=e()}(this,function(){"use strict";function C(e,t){var i,a=Object.keys(e);return Object.getOwnPropertySymbols&&(i=Object.getOwnPropertySymbols(e),t&&(i=i.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),a.push.apply(a,i)),a}function S(a){for(var t=1;tt.length)&&(e=t.length);for(var i=0,a=new Array(e);it.width?3===i?o=t.height*e:h=t.width/e:3===i?h=t.width/e:o=t.height*e,{aspectRatio:e,naturalWidth:n,naturalHeight:a,width:o,height:h});this.canvasData=e,this.limited=1===i||2===i,this.limitCanvas(!0,!0),e.width=Math.min(Math.max(e.width,e.minWidth),e.maxWidth),e.height=Math.min(Math.max(e.height,e.minHeight),e.maxHeight),e.left=(t.width-e.width)/2,e.top=(t.height-e.height)/2,e.oldLeft=e.left,e.oldTop=e.top,this.initialCanvasData=g({},e)},limitCanvas:function(t,e){var i=this.options,a=this.containerData,n=this.canvasData,o=this.cropBoxData,h=i.viewMode,r=n.aspectRatio,s=this.cropped&&o;t&&(t=Number(i.minCanvasWidth)||0,i=Number(i.minCanvasHeight)||0,1=a.width&&(n.minLeft=Math.min(0,r),n.maxLeft=Math.max(0,r)),n.height>=a.height)&&(n.minTop=Math.min(0,t),n.maxTop=Math.max(0,t))):(n.minLeft=-n.width,n.minTop=-n.height,n.maxLeft=a.width,n.maxTop=a.height))},renderCanvas:function(t,e){var i,a,n,o,h=this.canvasData,r=this.imageData;e&&(e={width:r.naturalWidth*Math.abs(r.scaleX||1),height:r.naturalHeight*Math.abs(r.scaleY||1),degree:r.rotate||0},r=e.width,o=e.height,e=e.degree,i=90==(e=Math.abs(e)%180)?{width:o,height:r}:(a=e%90*Math.PI/180,i=Math.sin(a),n=r*(a=Math.cos(a))+o*i,r=r*i+o*a,90h.maxWidth||h.widthh.maxHeight||h.heighte.width?a.height=a.width/i:a.width=a.height*i),this.cropBoxData=a,this.limitCropBox(!0,!0),a.width=Math.min(Math.max(a.width,a.minWidth),a.maxWidth),a.height=Math.min(Math.max(a.height,a.minHeight),a.maxHeight),a.width=Math.max(a.minWidth,a.width*t),a.height=Math.max(a.minHeight,a.height*t),a.left=e.left+(e.width-a.width)/2,a.top=e.top+(e.height-a.height)/2,a.oldLeft=a.left,a.oldTop=a.top,this.initialCropBoxData=g({},a)},limitCropBox:function(t,e){var i,a,n=this.options,o=this.containerData,h=this.canvasData,r=this.cropBoxData,s=this.limited,c=n.aspectRatio;t&&(t=Number(n.minCropBoxWidth)||0,n=Number(n.minCropBoxHeight)||0,i=s?Math.min(o.width,h.width,h.width+h.left,o.width-h.left):o.width,a=s?Math.min(o.height,h.height,h.height+h.top,o.height-h.top):o.height,t=Math.min(t,o.width),n=Math.min(n,o.height),c&&(t&&n?ti.maxWidth||i.widthi.maxHeight||i.height=e.width&&i.height>=e.height?q:I),f(this.cropBox,g({width:i.width,height:i.height},x({translateX:i.left,translateY:i.top}))),this.cropped&&this.limited&&this.limitCanvas(!0,!0),this.disabled||this.output()},output:function(){this.preview(),y(this.element,tt,this.getData())}},i={initPreview:function(){var t=this.element,i=this.crossOrigin,e=this.options.preview,a=i?this.crossOriginUrl:this.url,n=t.alt||"The image to preview",o=document.createElement("img");i&&(o.crossOrigin=i),o.src=a,o.alt=n,this.viewBox.appendChild(o),this.viewBoxImage=o,e&&("string"==typeof(o=e)?o=t.ownerDocument.querySelectorAll(e):e.querySelector&&(o=[e]),z(this.previews=o,function(t){var e=document.createElement("img");w(t,m,{width:t.offsetWidth,height:t.offsetHeight,html:t.innerHTML}),i&&(e.crossOrigin=i),e.src=a,e.alt=n,e.style.cssText='display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;"',t.innerHTML="",t.appendChild(e)}))},resetPreview:function(){z(this.previews,function(e){var i=Bt(e,m),i=(f(e,{width:i.width,height:i.height}),e.innerHTML=i.html,e),e=m;if(o(i[e]))try{delete i[e]}catch(t){i[e]=void 0}else if(i.dataset)try{delete i.dataset[e]}catch(t){i.dataset[e]=void 0}else i.removeAttribute("data-".concat(Dt(e)))})},preview:function(){var h=this.imageData,t=this.canvasData,e=this.cropBoxData,r=e.width,s=e.height,c=h.width,d=h.height,l=e.left-t.left-h.left,p=e.top-t.top-h.top;this.cropped&&!this.disabled&&(f(this.viewBoxImage,g({width:c,height:d},x(g({translateX:-l,translateY:-p},h)))),z(this.previews,function(t){var e=Bt(t,m),i=e.width,e=e.height,a=i,n=e,o=1;r&&(n=s*(o=i/r)),s&&eMath.abs(a-1)?i:a)&&(t.restore&&(o=this.getCanvasData(),h=this.getCropBoxData()),this.render(),t.restore)&&(this.setCanvasData(z(o,function(t,e){o[e]=t*n})),this.setCropBoxData(z(h,function(t,e){h[e]=t*n}))))},dblclick:function(){var t,e;this.disabled||this.options.dragMode===_||this.setDragMode((t=this.dragBox,e=Q,(t.classList?t.classList.contains(e):-1y&&(D.x=y-f);break;case k:p+D.xx&&(D.y=x-v)}}var i,a,o,n=this.options,h=this.canvasData,r=this.containerData,s=this.cropBoxData,c=this.pointers,d=this.action,l=n.aspectRatio,p=s.left,m=s.top,u=s.width,g=s.height,f=p+u,v=m+g,w=0,b=0,y=r.width,x=r.height,M=!0,C=(!l&&t.shiftKey&&(l=u&&g?u/g:1),this.limited&&(w=s.minLeft,b=s.minTop,y=w+Math.min(r.width,h.width,h.left+h.width),x=b+Math.min(r.height,h.height,h.top+h.height)),c[Object.keys(c)[0]]),D={x:C.endX-C.startX,y:C.endY-C.startY};switch(d){case I:p+=D.x,m+=D.y;break;case B:0<=D.x&&(y<=f||l&&(m<=b||x<=v))?M=!1:(e(B),(u+=D.x)<0&&(d=k,p-=u=-u),l&&(m+=(s.height-(g=u/l))/2));break;case T:D.y<=0&&(m<=b||l&&(p<=w||y<=f))?M=!1:(e(T),g-=D.y,m+=D.y,g<0&&(d=O,m-=g=-g),l&&(p+=(s.width-(u=g*l))/2));break;case k:D.x<=0&&(p<=w||l&&(m<=b||x<=v))?M=!1:(e(k),u-=D.x,p+=D.x,u<0&&(d=B,p-=u=-u),l&&(m+=(s.height-(g=u/l))/2));break;case O:0<=D.y&&(x<=v||l&&(p<=w||y<=f))?M=!1:(e(O),(g+=D.y)<0&&(d=T,m-=g=-g),l&&(p+=(s.width-(u=g*l))/2));break;case E:if(l){if(D.y<=0&&(m<=b||y<=f)){M=!1;break}e(T),g-=D.y,m+=D.y,u=g*l}else e(T),e(B),!(0<=D.x)||fMath.abs(o)&&(o=i)})}),o),t),M=!1;break;case U:D.x&&D.y?(i=Wt(this.cropper),p=C.startX-i.left,m=C.startY-i.top,u=s.minWidth,g=s.minHeight,0 or element.");this.element=t,this.options=g({},ut,u(e)&&e),this.cropped=!1,this.disabled=!1,this.pointers={},this.ready=!1,this.reloading=!1,this.replaced=!1,this.sized=!1,this.sizing=!1,this.init()}var t,e,i;return t=n,i=[{key:"noConflict",value:function(){return window.Cropper=Pt,n}},{key:"setDefaults",value:function(t){g(ut,u(t)&&t)}}],(e=[{key:"init",value:function(){var t,e=this.element,i=e.tagName.toLowerCase();if(!e[c]){if(e[c]=this,"img"===i){if(this.isImg=!0,t=e.getAttribute("src")||"",!(this.originalUrl=t))return;t=e.src}else"canvas"===i&&window.HTMLCanvasElement&&(t=e.toDataURL());this.load(t)}}},{key:"load",value:function(t){var e,i,a,n,o,h,r=this;t&&(this.url=t,this.imageData={},e=this.element,(i=this.options).rotatable||i.scalable||(i.checkOrientation=!1),i.checkOrientation&&window.ArrayBuffer?lt.test(t)?pt.test(t)?this.read((h=(h=t).replace(Xt,""),a=atob(h),h=new ArrayBuffer(a.length),z(n=new Uint8Array(h),function(t,e){n[e]=a.charCodeAt(e)}),h)):this.clone():(o=new XMLHttpRequest,h=this.clone.bind(this),this.reloading=!0,(this.xhr=o).onabort=h,o.onerror=h,o.ontimeout=h,o.onprogress=function(){o.getResponseHeader("content-type")!==ct&&o.abort()},o.onload=function(){r.read(o.response)},o.onloadend=function(){r.reloading=!1,r.xhr=null},i.checkCrossOrigin&&Lt(t)&&e.crossOrigin&&(t=zt(t)),o.open("GET",t,!0),o.responseType="arraybuffer",o.withCredentials="use-credentials"===e.crossOrigin,o.send()):this.clone())}},{key:"read",value:function(t){var e=this.options,i=this.imageData,a=Rt(t),n=0,o=1,h=1;1
',o=(n=n.querySelector(".".concat(c,"-container"))).querySelector(".".concat(c,"-canvas")),h=n.querySelector(".".concat(c,"-drag-box")),s=(r=n.querySelector(".".concat(c,"-crop-box"))).querySelector(".".concat(c,"-face")),this.container=a,this.cropper=n,this.canvas=o,this.dragBox=h,this.cropBox=r,this.viewBox=n.querySelector(".".concat(c,"-view-box")),this.face=s,o.appendChild(i),v(t,L),a.insertBefore(n,t.nextSibling),X(i,Z),this.initPreview(),this.bind(),e.initialAspectRatio=Math.max(0,e.initialAspectRatio)||NaN,e.aspectRatio=Math.max(0,e.aspectRatio)||NaN,e.viewMode=Math.max(0,Math.min(3,Math.round(e.viewMode)))||0,v(r,L),e.guides||v(r.getElementsByClassName("".concat(c,"-dashed")),L),e.center||v(r.getElementsByClassName("".concat(c,"-center")),L),e.background&&v(n,"".concat(c,"-bg")),e.highlight||v(s,G),e.cropBoxMovable&&(v(s,V),w(s,d,I)),e.cropBoxResizable||(v(r.getElementsByClassName("".concat(c,"-line")),L),v(r.getElementsByClassName("".concat(c,"-point")),L)),this.render(),this.ready=!0,this.setDragMode(e.dragMode),e.autoCrop&&this.crop(),this.setData(e.data),l(e.ready)&&b(t,"ready",e.ready,{once:!0}),y(t,"ready"))}},{key:"unbuild",value:function(){var t;this.ready&&(this.ready=!1,this.unbind(),this.resetPreview(),(t=this.cropper.parentNode)&&t.removeChild(this.cropper),X(this.element,L))}},{key:"uncreate",value:function(){this.ready?(this.unbuild(),this.ready=!1,this.cropped=!1):this.sizing?(this.sizingImage.onload=null,this.sizing=!1,this.sized=!1):this.reloading?(this.xhr.onabort=null,this.xhr.abort()):this.image&&this.stop()}}])&&j(t.prototype,e),i&&j(t,i),Object.defineProperty(t,"prototype",{writable:!1}),n}();return g(It.prototype,t,i,e,St,jt,At),It}); \ No newline at end of file diff --git a/app/assets/javascripts/fabric.min.js b/app/assets/javascripts/fabric.min.js new file mode 100644 index 000000000..9455a6f0e --- /dev/null +++ b/app/assets/javascripts/fabric.min.js @@ -0,0 +1 @@ +var fabric=fabric||{version:"5.2.1"};if("undefined"!=typeof exports?exports.fabric=fabric:"function"==typeof define&&define.amd&&define([],function(){return fabric}),"undefined"!=typeof document&&"undefined"!=typeof window)fabric.document=document instanceof("undefined"!=typeof HTMLDocument?HTMLDocument:Document)?document:document.implementation.createHTMLDocument(""),fabric.window=window;else{var jsdom=require("jsdom"),virtualWindow=new jsdom.JSDOM(decodeURIComponent("%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E"),{features:{FetchExternalResources:["img"]},resources:"usable"}).window;fabric.document=virtualWindow.document,fabric.jsdomImplForWrapper=require("jsdom/lib/jsdom/living/generated/utils").implForWrapper,fabric.nodeCanvas=require("jsdom/lib/jsdom/utils").Canvas,fabric.window=virtualWindow,DOMParser=fabric.window.DOMParser}fabric.isTouchSupported="ontouchstart"in fabric.window||"ontouchstart"in fabric.document||fabric.window&&fabric.window.navigator&&fabric.window.navigator.maxTouchPoints>0,fabric.isLikelyNode="undefined"!=typeof Buffer&&"undefined"==typeof window,fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-dashoffset","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","id","paint-order","vector-effect","instantiated_by_use","clip-path"],fabric.DPI=96,fabric.reNum="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:[eE][-+]?\\d+)?)",fabric.commaWsp="(?:\\s+,?\\s*|,\\s*)",fabric.rePathCommand=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:[eE][-+]?\d+)?)/gi,fabric.reNonWord=/[ \n\.,;!\?\-]/,fabric.fontPaths={},fabric.iMatrix=[1,0,0,1,0,0],fabric.svgNS="http://www.w3.org/2000/svg",fabric.perfLimitSizeTotal=2097152,fabric.maxCacheSideLimit=4096,fabric.minCacheSideLimit=256,fabric.charWidthsCache={},fabric.textureSize=2048,fabric.disableStyleCopyPaste=!1,fabric.enableGLFiltering=!0,fabric.devicePixelRatio=fabric.window.devicePixelRatio||fabric.window.webkitDevicePixelRatio||fabric.window.mozDevicePixelRatio||1,fabric.browserShadowBlurConstant=1,fabric.arcToSegmentsCache={},fabric.boundsOfCurveCache={},fabric.cachesBoundsOfCurve=!0,fabric.forceGLPutImageData=!1,fabric.initFilterBackend=function(){return fabric.enableGLFiltering&&fabric.isWebglSupported&&fabric.isWebglSupported(fabric.textureSize)?(console.log("max texture size: "+fabric.maxTextureSize),new fabric.WebglFilterBackend({tileSize:fabric.textureSize})):fabric.Canvas2dFilterBackend?new fabric.Canvas2dFilterBackend:void 0};"undefined"!=typeof document&&"undefined"!=typeof window&&(window.fabric=fabric);!function(){function t(t,e){if(this.__eventListeners[t]){var r=this.__eventListeners[t];e?r[r.indexOf(e)]=!1:fabric.util.array.fill(r,!1)}}function e(t,e){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var r in t)this.on(r,t[r]);else this.__eventListeners[t]||(this.__eventListeners[t]=[]),this.__eventListeners[t].push(e);return this}function r(t,e){var r=function(){e.apply(this,arguments),this.off(t,r)}.bind(this);this.on(t,r)}function n(t,e){if(1===arguments.length)for(var n in t)r.call(this,n,t[n]);else r.call(this,t,e);return this}function i(e,r){if(!this.__eventListeners)return this;if(0===arguments.length)for(e in this.__eventListeners)t.call(this,e);else if(1===arguments.length&&"object"==typeof arguments[0])for(var n in e)t.call(this,n,e[n]);else t.call(this,e,r);return this}function a(t,e){if(!this.__eventListeners)return this;var r=this.__eventListeners[t];if(!r)return this;for(var n=0,i=r.length;i>n;n++)r[n]&&r[n].call(this,e||{});return this.__eventListeners[t]=r.filter(function(t){return t!==!1}),this}fabric.Observable={fire:a,on:e,once:n,off:i}}();fabric.Collection={_objects:[],add:function(){if(this._objects.push.apply(this._objects,arguments),this._onObjectAdded)for(var e=0,t=arguments.length;t>e;e++)this._onObjectAdded(arguments[e]);return this.renderOnAddRemove&&this.requestRenderAll(),this},insertAt:function(e,t,r){var i=this._objects;return r?i[t]=e:i.splice(t,0,e),this._onObjectAdded&&this._onObjectAdded(e),this.renderOnAddRemove&&this.requestRenderAll(),this},remove:function(){for(var e,t=this._objects,r=!1,i=0,n=arguments.length;n>i;i++)e=t.indexOf(arguments[i]),-1!==e&&(r=!0,t.splice(e,1),this._onObjectRemoved&&this._onObjectRemoved(arguments[i]));return this.renderOnAddRemove&&r&&this.requestRenderAll(),this},forEachObject:function(e,t){for(var r=this.getObjects(),i=0,n=r.length;n>i;i++)e.call(t,r[i],i,r);return this},getObjects:function(e){return"undefined"==typeof e?this._objects.concat():this._objects.filter(function(t){return t.type===e})},item:function(e){return this._objects[e]},isEmpty:function(){return 0===this._objects.length},size:function(){return this._objects.length},contains:function(e,t){return this._objects.indexOf(e)>-1?!0:t?this._objects.some(function(t){return"function"==typeof t.contains&&t.contains(e,!0)}):!1},complexity:function(){return this._objects.reduce(function(e,t){return e+=t.complexity?t.complexity():0},0)}};fabric.CommonMethods={_setOptions:function(t){for(var e in t)this.set(e,t[e])},_initGradient:function(t,e){!t||!t.colorStops||t instanceof fabric.Gradient||this.set(e,new fabric.Gradient(t))},_initPattern:function(t,e,r){!t||!t.source||t instanceof fabric.Pattern?r&&r():this.set(e,new fabric.Pattern(t,r))},_setObject:function(t){for(var e in t)this._set(e,t[e])},set:function(t,e){return"object"==typeof t?this._setObject(t):this._set(t,e),this},_set:function(t,e){this[t]=e},toggle:function(t){var e=this.get(t);return"boolean"==typeof e&&this.set(t,!e),this},get:function(t){return this[t]}};!function(e){var t=Math.sqrt,r=Math.atan2,i=Math.pow,a=Math.PI/180,n=Math.PI/2;fabric.util={cos:function(e){if(0===e)return 1;0>e&&(e=-e);var t=e/n;switch(t){case 1:case 3:return 0;case 2:return-1}return Math.cos(e)},sin:function(e){if(0===e)return 0;var t=e/n,r=1;switch(0>e&&(r=-1),t){case 1:return r;case 2:return 0;case 3:return-r}return Math.sin(e)},removeFromArray:function(e,t){var r=e.indexOf(t);return-1!==r&&e.splice(r,1),e},getRandomInt:function(e,t){return Math.floor(Math.random()*(t-e+1))+e},degreesToRadians:function(e){return e*a},radiansToDegrees:function(e){return e/a},rotatePoint:function(e,t,r){var i=new fabric.Point(e.x-t.x,e.y-t.y),a=fabric.util.rotateVector(i,r);return new fabric.Point(a.x,a.y).addEquals(t)},rotateVector:function(e,t){var r=fabric.util.sin(t),i=fabric.util.cos(t),a=e.x*i-e.y*r,n=e.x*r+e.y*i;return{x:a,y:n}},createVector:function(e,t){return new fabric.Point(t.x-e.x,t.y-e.y)},calcAngleBetweenVectors:function(e,t){return Math.acos((e.x*t.x+e.y*t.y)/(Math.hypot(e.x,e.y)*Math.hypot(t.x,t.y)))},getHatVector:function(e){return new fabric.Point(e.x,e.y).multiply(1/Math.hypot(e.x,e.y))},getBisector:function(e,t,r){var i=fabric.util.createVector(e,t),a=fabric.util.createVector(e,r),n=fabric.util.calcAngleBetweenVectors(i,a),o=fabric.util.calcAngleBetweenVectors(fabric.util.rotateVector(i,n),a),c=n*(0===o?1:-1)/2;return{vector:fabric.util.getHatVector(fabric.util.rotateVector(i,c)),angle:n}},projectStrokeOnPoints:function(e,t,r){var i=[],a=t.strokeWidth/2,n=t.strokeUniform?new fabric.Point(1/t.scaleX,1/t.scaleY):new fabric.Point(1,1),o=function(e){var t=a/Math.hypot(e.x,e.y);return new fabric.Point(e.x*t*n.x,e.y*t*n.y)};return e.length<=1?i:(e.forEach(function(c,f){var l,s,u=new fabric.Point(c.x,c.y);0===f?(s=e[f+1],l=r?o(fabric.util.createVector(s,u)).addEquals(u):e[e.length-1]):f===e.length-1?(l=e[f-1],s=r?o(fabric.util.createVector(l,u)).addEquals(u):e[0]):(l=e[f-1],s=e[f+1]);var d,b,m=fabric.util.getBisector(u,l,s),h=m.vector,p=m.angle;return"miter"===t.strokeLineJoin&&(d=-a/Math.sin(p/2),b=new fabric.Point(h.x*d*n.x,h.y*d*n.y),Math.hypot(b.x,b.y)/a<=t.strokeMiterLimit)?(i.push(u.add(b)),void i.push(u.subtract(b))):(d=-a*Math.SQRT2,b=new fabric.Point(h.x*d*n.x,h.y*d*n.y),i.push(u.add(b)),void i.push(u.subtract(b)))}),i)},transformPoint:function(e,t,r){return r?new fabric.Point(t[0]*e.x+t[2]*e.y,t[1]*e.x+t[3]*e.y):new fabric.Point(t[0]*e.x+t[2]*e.y+t[4],t[1]*e.x+t[3]*e.y+t[5])},makeBoundingBoxFromPoints:function(e,t){if(t)for(var r=0;rr;++r)n=n[i[r]];return n},loadImage:function(e,t,r,i){if(!e)return void(t&&t.call(r,e));var a=fabric.util.createImage(),n=function(){t&&t.call(r,a,!1),a=a.onload=a.onerror=null};a.onload=n,a.onerror=function(){fabric.log("Error loading "+a.src),t&&t.call(r,null,!0),a=a.onload=a.onerror=null},0!==e.indexOf("data")&&void 0!==i&&null!==i&&(a.crossOrigin=i),"data:image/svg"===e.substring(0,14)&&(a.onload=null,fabric.util.loadImageInDom(a,n)),a.src=e},loadImageInDom:function(e,t){var r=fabric.document.createElement("div");r.style.width=r.style.height="1px",r.style.left=r.style.top="-100%",r.style.position="absolute",r.appendChild(e),fabric.document.querySelector("body").appendChild(r),e.onload=function(){t(),r.parentNode.removeChild(r),r=null}},enlivenObjects:function(e,t,r,i){function a(){++o===c&&t&&t(n.filter(function(e){return e}))}e=e||[];var n=[],o=0,c=e.length;return c?void e.forEach(function(e,t){if(!e||!e.type)return void a();var o=fabric.util.getKlass(e.type,r);o.fromObject(e,function(r,o){o||(n[t]=r),i&&i(e,r,o),a()})}):void(t&&t(n))},enlivenObjectEnlivables:function(e,t,r){var i=fabric.Object.ENLIVEN_PROPS.filter(function(t){return!!e[t]});fabric.util.enlivenObjects(i.map(function(t){return e[t]}),function(e){var a={};i.forEach(function(r,i){a[r]=e[i],t&&(t[r]=e[i])}),r&&r(a)})},enlivenPatterns:function(e,t){function r(){++a===n&&t&&t(i)}e=e||[];var i=[],a=0,n=e.length;return n?void e.forEach(function(e,t){e&&e.source?new fabric.Pattern(e,function(e){i[t]=e,r()}):(i[t]=e,r())}):void(t&&t(i))},groupSVGElements:function(e,t,r){var i;return e&&1===e.length?e[0]:(t&&(t.width&&t.height?t.centerPoint={x:t.width/2,y:t.height/2}:(delete t.width,delete t.height)),i=new fabric.Group(e,t),"undefined"!=typeof r&&(i.sourcePath=r),i)},populateWithProperties:function(e,t,r){if(r&&Array.isArray(r))for(var i=0,a=r.length;a>i;i++)r[i]in e&&(t[r[i]]=e[r[i]])},createCanvasElement:function(){return fabric.document.createElement("canvas")},copyCanvasElement:function(e){var t=fabric.util.createCanvasElement();return t.width=e.width,t.height=e.height,t.getContext("2d").drawImage(e,0,0),t},toDataURL:function(e,t,r){return e.toDataURL("image/"+t,r)},createImage:function(){return fabric.document.createElement("img")},multiplyTransformMatrices:function(e,t,r){return[e[0]*t[0]+e[2]*t[1],e[1]*t[0]+e[3]*t[1],e[0]*t[2]+e[2]*t[3],e[1]*t[2]+e[3]*t[3],r?0:e[0]*t[4]+e[2]*t[5]+e[4],r?0:e[1]*t[4]+e[3]*t[5]+e[5]]},qrDecompose:function(e){var n=r(e[1],e[0]),o=i(e[0],2)+i(e[1],2),c=t(o),f=(e[0]*e[3]-e[2]*e[1])/c,l=r(e[0]*e[2]+e[1]*e[3],o);return{angle:n/a,scaleX:c,scaleY:f,skewX:l/a,skewY:0,translateX:e[4],translateY:e[5]}},calcRotateMatrix:function(e){if(!e.angle)return fabric.iMatrix.concat();var t=fabric.util.degreesToRadians(e.angle),r=fabric.util.cos(t),i=fabric.util.sin(t);return[r,i,-i,r,0,0]},calcDimensionsMatrix:function(e){var t="undefined"==typeof e.scaleX?1:e.scaleX,r="undefined"==typeof e.scaleY?1:e.scaleY,i=[e.flipX?-t:t,0,0,e.flipY?-r:r,0,0],a=fabric.util.multiplyTransformMatrices,n=fabric.util.degreesToRadians;return e.skewX&&(i=a(i,[1,0,Math.tan(n(e.skewX)),1],!0)),e.skewY&&(i=a(i,[1,Math.tan(n(e.skewY)),0,1],!0)),i},composeMatrix:function(e){var t=[1,0,0,1,e.translateX||0,e.translateY||0],r=fabric.util.multiplyTransformMatrices;return e.angle&&(t=r(t,fabric.util.calcRotateMatrix(e))),(1!==e.scaleX||1!==e.scaleY||e.skewX||e.skewY||e.flipX||e.flipY)&&(t=r(t,fabric.util.calcDimensionsMatrix(e))),t},resetObjectTransform:function(e){e.scaleX=1,e.scaleY=1,e.skewX=0,e.skewY=0,e.flipX=!1,e.flipY=!1,e.rotate(0)},saveObjectTransform:function(e){return{scaleX:e.scaleX,scaleY:e.scaleY,skewX:e.skewX,skewY:e.skewY,angle:e.angle,left:e.left,flipX:e.flipX,flipY:e.flipY,top:e.top}},isTransparent:function(e,t,r,i){i>0&&(t>i?t-=i:t=0,r>i?r-=i:r=0);var a,n,o=!0,c=e.getImageData(t,r,2*i||1,2*i||1),f=c.data.length;for(a=3;f>a&&(n=c.data[a],o=0>=n,o!==!1);a+=4);return c=null,o},parsePreserveAspectRatioAttribute:function(e){var t,r="meet",i="Mid",a="Mid",n=e.split(" ");return n&&n.length&&(r=n.pop(),"meet"!==r&&"slice"!==r?(t=r,r="meet"):n.length&&(t=n.pop())),i="none"!==t?t.slice(1,4):"none",a="none"!==t?t.slice(5,8):"none",{meetOrSlice:r,alignX:i,alignY:a}},clearFabricFontCache:function(e){e=(e||"").toLowerCase(),e?fabric.charWidthsCache[e]&&delete fabric.charWidthsCache[e]:fabric.charWidthsCache={}},limitDimsByArea:function(e,t){var r=Math.sqrt(t*e),i=Math.floor(t/r);return{x:Math.floor(r),y:i}},capValue:function(e,t,r){return Math.max(e,Math.min(t,r))},findScaleToFit:function(e,t){return Math.min(t.width/e.width,t.height/e.height)},findScaleToCover:function(e,t){return Math.max(t.width/e.width,t.height/e.height)},matrixToSVG:function(e){return"matrix("+e.map(function(e){return fabric.util.toFixed(e,fabric.Object.NUM_FRACTION_DIGITS)}).join(" ")+")"},removeTransformFromObject:function(e,t){var r=fabric.util.invertTransform(t),i=fabric.util.multiplyTransformMatrices(r,e.calcOwnMatrix());fabric.util.applyTransformToObject(e,i)},addTransformToObject:function(e,t){fabric.util.applyTransformToObject(e,fabric.util.multiplyTransformMatrices(t,e.calcOwnMatrix()))},applyTransformToObject:function(e,t){var r=fabric.util.qrDecompose(t),i=new fabric.Point(r.translateX,r.translateY);e.flipX=!1,e.flipY=!1,e.set("scaleX",r.scaleX),e.set("scaleY",r.scaleY),e.skewX=r.skewX,e.skewY=r.skewY,e.angle=r.angle,e.setPositionByOrigin(i,"center","center")},sizeAfterTransform:function(e,t,r){var i=e/2,a=t/2,n=[{x:-i,y:-a},{x:i,y:-a},{x:-i,y:a},{x:i,y:a}],o=fabric.util.calcDimensionsMatrix(r),c=fabric.util.makeBoundingBoxFromPoints(n,o);return{x:c.width,y:c.height}},mergeClipPaths:function(e,t){var r=e,i=t;r.inverted&&!i.inverted&&(r=t,i=e),fabric.util.applyTransformToObject(i,fabric.util.multiplyTransformMatrices(fabric.util.invertTransform(r.calcTransformMatrix()),i.calcTransformMatrix()));var a=r.inverted&&i.inverted;return a&&(r.inverted=i.inverted=!1),new fabric.Group([r],{clipPath:i,inverted:a})},hasStyleChanged:function(e,t,r){return r=r||!1,e.fill!==t.fill||e.stroke!==t.stroke||e.strokeWidth!==t.strokeWidth||e.fontSize!==t.fontSize||e.fontFamily!==t.fontFamily||e.fontWeight!==t.fontWeight||e.fontStyle!==t.fontStyle||e.deltaY!==t.deltaY||r&&(e.overline!==t.overline||e.underline!==t.underline||e.linethrough!==t.linethrough)},stylesToArray:function(e,t){for(var e=fabric.util.object.clone(e,!0),r=t.split("\n"),i=-1,a={},n=[],o=0;ow){var P=Math.sqrt(1-w/(y*v));i*=P,a*=P}else M=(c===o?-1:1)*Math.sqrt(w/(y*g+v*x));var C=M*i*p/a,k=-M*a*m/i,T=d*C-l*k+.5*e,j=l*C+d*k+.5*n,O=r(1,0,(m-C)/i,(p-k)/a),S=r((m-C)/i,(p-k)/a,(-m-C)/i,(-p-k)/a);0===o&&S>0?S-=2*f:1===o&&0>S&&(S+=2*f);for(var E=Math.ceil(Math.abs(S/f*2)),F=[],Y=S/E,A=8/3*Math.sin(Y/4)*Math.sin(Y/4)/Math.sin(Y/2),D=O+Y,X=0;E>X;X++)F[X]=t(O,D,d,l,i,a,T,j,A,h,b),h=F[X][5],b=F[X][6],O=D,D+=Y;return F}function r(t,e,r,n){var i=Math.atan2(e,t),a=Math.atan2(n,r);return a>=i?a-i:2*Math.PI-(i-a)}function n(t,e,r,n,i,a,c,o){var s;if(fabric.cachesBoundsOfCurve&&(s=k.call(arguments),fabric.boundsOfCurveCache[s]))return fabric.boundsOfCurveCache[s];var f,u,l,d,h,b,m,p,y=Math.sqrt,v=Math.min,g=Math.max,x=Math.abs,w=[],M=[[],[]];u=6*t-12*r+6*i,f=-3*t+9*r-9*i+3*c,l=3*r-3*t;for(var P=0;2>P;++P)if(P>0&&(u=6*e-12*n+6*a,f=-3*e+9*n-9*a+3*o,l=3*n-3*e),x(f)<1e-12){if(x(u)<1e-12)continue;d=-l/u,d>0&&1>d&&w.push(d)}else m=u*u-4*l*f,0>m||(p=y(m),h=(-u+p)/(2*f),h>0&&1>h&&w.push(h),b=(-u-p)/(2*f),b>0&&1>b&&w.push(b));for(var C,T,j,O=w.length,S=O;O--;)d=w[O],j=1-d,C=j*j*j*t+3*j*j*d*r+3*j*d*d*i+d*d*d*c,M[0][O]=C,T=j*j*j*e+3*j*j*d*n+3*j*d*d*a+d*d*d*o,M[1][O]=T;M[0][S]=t,M[1][S]=e,M[0][S+1]=c,M[1][S+1]=o;var E=[{x:v.apply(null,M[0]),y:v.apply(null,M[1])},{x:g.apply(null,M[0]),y:g.apply(null,M[1])}];return fabric.cachesBoundsOfCurve&&(fabric.boundsOfCurveCache[s]=E),E}function i(t,r,n){for(var i=n[1],a=n[2],c=n[3],o=n[4],s=n[5],f=n[6],u=n[7],l=e(f-t,u-r,i,a,o,s,c),d=0,h=l.length;h>d;d++)l[d][1]+=t,l[d][2]+=r,l[d][3]+=t,l[d][4]+=r,l[d][5]+=t,l[d][6]+=r;return l}function a(t){var e,r,n,a,c,o,s=0,f=0,u=t.length,l=0,d=0,h=[];for(r=0;u>r;++r){switch(n=!1,e=t[r].slice(0),e[0]){case"l":e[0]="L",e[1]+=s,e[2]+=f;case"L":s=e[1],f=e[2];break;case"h":e[1]+=s;case"H":e[0]="L",e[2]=f,s=e[1];break;case"v":e[1]+=f;case"V":e[0]="L",f=e[1],e[1]=s,e[2]=f;break;case"m":e[0]="M",e[1]+=s,e[2]+=f;case"M":s=e[1],f=e[2],l=e[1],d=e[2];break;case"c":e[0]="C",e[1]+=s,e[2]+=f,e[3]+=s,e[4]+=f,e[5]+=s,e[6]+=f;case"C":c=e[3],o=e[4],s=e[5],f=e[6];break;case"s":e[0]="S",e[1]+=s,e[2]+=f,e[3]+=s,e[4]+=f;case"S":"C"===a?(c=2*s-c,o=2*f-o):(c=s,o=f),s=e[3],f=e[4],e[0]="C",e[5]=e[3],e[6]=e[4],e[3]=e[1],e[4]=e[2],e[1]=c,e[2]=o,c=e[3],o=e[4];break;case"q":e[0]="Q",e[1]+=s,e[2]+=f,e[3]+=s,e[4]+=f;case"Q":c=e[1],o=e[2],s=e[3],f=e[4];break;case"t":e[0]="T",e[1]+=s,e[2]+=f;case"T":"Q"===a?(c=2*s-c,o=2*f-o):(c=s,o=f),e[0]="Q",s=e[1],f=e[2],e[1]=c,e[2]=o,e[3]=s,e[4]=f;break;case"a":e[0]="A",e[6]+=s,e[7]+=f;case"A":n=!0,h=h.concat(i(s,f,e)),s=e[6],f=e[7];break;case"z":case"Z":s=l,f=d}n||h.push(e),a=e[0]}return h}function c(t,e,r,n){return Math.sqrt((r-t)*(r-t)+(n-e)*(n-e))}function o(t){return t*t*t}function s(t){return 3*t*t*(1-t)}function f(t){return 3*t*(1-t)*(1-t)}function u(t){return(1-t)*(1-t)*(1-t)}function l(t,e,r,n,i,a,c,l){return function(d){var h=o(d),b=s(d),m=f(d),p=u(d);return{x:c*h+i*b+r*m+t*p,y:l*h+a*b+n*m+e*p}}}function d(t,e,r,n,i,a,c,o){return function(s){var f=1-s,u=3*f*f*(r-t)+6*f*s*(i-r)+3*s*s*(c-i),l=3*f*f*(n-e)+6*f*s*(a-n)+3*s*s*(o-a);return Math.atan2(l,u)}}function h(t){return t*t}function b(t){return 2*t*(1-t)}function m(t){return(1-t)*(1-t)}function p(t,e,r,n,i,a){return function(c){var o=h(c),s=b(c),f=m(c);return{x:i*o+r*s+t*f,y:a*o+n*s+e*f}}}function y(t,e,r,n,i,a){return function(c){var o=1-c,s=2*o*(r-t)+2*c*(i-r),f=2*o*(n-e)+2*c*(a-n);return Math.atan2(f,s)}}function v(t,e,r){var n,i,a={x:e,y:r},o=0;for(i=1;100>=i;i+=1)n=t(i/100),o+=c(a.x,a.y,n.x,n.y),a=n;return o}function g(t,e){for(var r,n,i,a=0,o=0,s=t.iterator,f={x:t.x,y:t.y},u=.01,l=t.angleFinder;e>o&&u>1e-4;)r=s(a),i=a,n=c(f.x,f.y,r.x,r.y),n+o>e?(a-=u,u/=2):(f=r,a+=u,o+=n);return r.angle=l(i),r}function x(t){for(var e,r,n,i,a=0,o=t.length,s=0,f=0,u=0,h=0,b=[],m=0;o>m;m++){switch(e=t[m],n={x:s,y:f,command:e[0]},e[0]){case"M":n.length=0,u=s=e[1],h=f=e[2];break;case"L":n.length=c(s,f,e[1],e[2]),s=e[1],f=e[2];break;case"C":r=l(s,f,e[1],e[2],e[3],e[4],e[5],e[6]),i=d(s,f,e[1],e[2],e[3],e[4],e[5],e[6]),n.iterator=r,n.angleFinder=i,n.length=v(r,s,f),s=e[5],f=e[6];break;case"Q":r=p(s,f,e[1],e[2],e[3],e[4]),i=y(s,f,e[1],e[2],e[3],e[4]),n.iterator=r,n.angleFinder=i,n.length=v(r,s,f),s=e[3],f=e[4];break;case"Z":case"z":n.destX=u,n.destY=h,n.length=c(s,f,u,h),s=u,f=h}a+=n.length,b.push(n)}return b.push({length:a,x:s,y:f}),b}function w(t,e,r){r||(r=x(t));for(var n=0;e-r[n].length>0&&nm;m++){e=a[m],i=e.slice(1).trim(),o.length=0;var y=e.charAt(0);if(b=[y],"a"===y.toLowerCase())for(var v;v=h.exec(i);)for(var g=1;gg;g++)r=parseFloat(o[g]),isNaN(r)||b.push(r);var w=T[y.toLowerCase()],M=j[y]||y;if(b.length-1>w)for(var P=1,C=b.length;C>P;P+=w)c.push([y].concat(b.slice(P,P+w))),y=M;else c.push(b)}return c}function P(t,e){var r,n=[],i=new fabric.Point(t[0].x,t[0].y),a=new fabric.Point(t[1].x,t[1].y),c=t.length,o=1,s=0,f=c>2;for(e=e||0,f&&(o=t[2].xr;r++){if(!i.eq(a)){var u=i.midPointFrom(a);n.push(["Q",i.x,i.y,u.x,u.y])}i=t[r],r+1t[r-2].x?1:i.x===t[r-2].x?0:-1,s=i.y>t[r-2].y?1:i.y===t[r-2].y?0:-1),n.push(["L",i.x+o*e,i.y+s*e]),n}function C(t,e,r){return r&&(e=fabric.util.multiplyTransformMatrices(e,[1,0,0,1,-r.x,-r.y])),t.map(function(t){for(var r=t.slice(0),n={},i=1;ii;i++)n[i]=r.length?t[i][e].apply(t[i],r):t[i][e].call(t[i]);return n}function e(t,e){return i(t,e,function(t,e){return t>=e})}function r(t,e){return i(t,e,function(t,e){return e>t})}function n(t,e){for(var r=t.length;r--;)t[r]=e;return t}function i(t,e,r){if(t&&0!==t.length){var n=t.length-1,i=e?t[n][e]:t[n];if(e)for(;n--;)r(t[n][e],i)&&(i=t[n][e]);else for(;n--;)r(t[n],i)&&(i=t[n]);return i}}var a=Array.prototype.slice;fabric.util.array={fill:n,invoke:t,min:r,max:e}}();!function(){function t(e,r,n){if(n)if(!fabric.isLikelyNode&&r instanceof Element)e=r;else if(r instanceof Array){e=[];for(var i=0,a=r.length;a>i;i++)e[i]=t({},r[i],n)}else if(r&&"object"==typeof r)for(var c in r)"canvas"===c||"group"===c?e[c]=null:r.hasOwnProperty(c)&&(e[c]=t({},r[c],n));else e=r;else for(var c in r)e[c]=r[c];return e}function e(e,r){return t({},e,r)}fabric.util.object={extend:t,clone:e},fabric.util.object.extend(fabric.util,fabric.Observable)}();!function(){function t(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})}function e(t,e){return t.charAt(0).toUpperCase()+(e?t.slice(1):t.slice(1).toLowerCase())}function r(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function n(t){var e,r=0,n=[];for(r=0,e;rr||r>57343)return t.charAt(e);if(r>=55296&&56319>=r){if(t.length<=e+1)throw"High surrogate without following low surrogate";var n=t.charCodeAt(e+1);if(56320>n||n>57343)throw"High surrogate without following low surrogate";return t.charAt(e)+t.charAt(e+1)}if(0===e)throw"Low surrogate without preceding high surrogate";var i=t.charCodeAt(e-1);if(55296>i||i>56319)throw"Low surrogate without preceding high surrogate";return!1}fabric.util.string={camelize:t,capitalize:e,escapeXml:r,graphemeSplit:n}}();!function(){function t(){}function e(t){for(var e=null,r=this;r.constructor.superclass;){var i=r.constructor.superclass.prototype[t];if(r[t]!==i){e=i;break}r=r.constructor.superclass.prototype}return e?arguments.length>1?e.apply(this,n.call(arguments,1)):e.call(this):console.log("tried to callSuper "+t+", method not found in prototype chain",this)}function r(){function r(){this.initialize.apply(this,arguments)}var a=null,c=n.call(arguments,0);"function"==typeof c[0]&&(a=c.shift()),r.superclass=a,r.subclasses=[],a&&(t.prototype=a.prototype,r.prototype=new t,a.subclasses.push(r));for(var s=0,l=c.length;l>s;s++)o(r,c[s],a);return r.prototype.initialize||(r.prototype.initialize=i),r.prototype.constructor=r,r.prototype.callSuper=e,r}var n=Array.prototype.slice,i=function(){},a=function(){for(var t in{toString:1})if("toString"===t)return!1;return!0}(),o=function(t,e,r){for(var n in e)t.prototype[n]=n in t.prototype&&"function"==typeof t.prototype[n]&&(e[n]+"").indexOf("callSuper")>-1?function(t){return function(){var n=this.constructor.superclass;this.constructor.superclass=r;var i=e[t].apply(this,arguments);return this.constructor.superclass=n,"initialize"!==t?i:void 0}}(n):e[n],a&&(e.toString!==Object.prototype.toString&&(t.prototype.toString=e.toString),e.valueOf!==Object.prototype.valueOf&&(t.prototype.valueOf=e.valueOf))};fabric.util.createClass=r}();!function(){function t(t){var e=t.changedTouches;return e&&e[0]?e[0]:t}var e=!!fabric.document.createElement("div").attachEvent,r=["touchstart","touchmove","touchend"];fabric.util.addListener=function(t,r,i,n){t&&t.addEventListener(r,i,e?!1:n)},fabric.util.removeListener=function(t,r,i,n){t&&t.removeEventListener(r,i,e?!1:n)},fabric.util.getPointer=function(e){var r=e.target,i=fabric.util.getScrollLeftTop(r),n=t(e);return{x:n.clientX+i.left,y:n.clientY+i.top}},fabric.util.isTouchEvent=function(t){return r.indexOf(t.type)>-1||"touch"===t.pointerType}}();!function(){function t(t,e){var i=t.style;if(!i)return t;if("string"==typeof e)return t.style.cssText+=";"+e,e.indexOf("opacity")>-1?a(t,e.match(/opacity:\s*(\d?\.?\d*)/)[1]):t;for(var n in e)if("opacity"===n)a(t,e[n]);else{var r="float"===n||"cssFloat"===n?"undefined"==typeof i.styleFloat?"cssFloat":"styleFloat":n;i.setProperty(r,e[n])}return t}var e=fabric.document.createElement("div"),i="string"==typeof e.style.opacity,n="string"==typeof e.style.filter,r=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,a=function(t){return t};i?a=function(t,e){return t.style.opacity=e,t}:n&&(a=function(t,e){var i=t.style;return t.currentStyle&&!t.currentStyle.hasLayout&&(i.zoom=1),r.test(i.filter)?(e=e>=.9999?"":"alpha(opacity="+100*e+")",i.filter=i.filter.replace(r,e)):i.filter+=" alpha(opacity="+100*e+")",t}),fabric.util.setStyle=t}();!function(){function t(t){return"string"==typeof t?fabric.document.getElementById(t):t}function e(t,e){var i=fabric.document.createElement(t);for(var n in e)"class"===n?i.className=e[n]:"for"===n?i.htmlFor=e[n]:i.setAttribute(n,e[n]);return i}function i(t,e){t&&-1===(" "+t.className+" ").indexOf(" "+e+" ")&&(t.className+=(t.className?" ":"")+e)}function n(t,i,n){return"string"==typeof i&&(i=e(i,n)),t.parentNode&&t.parentNode.replaceChild(i,t),i.appendChild(t),i}function r(t){for(var e=0,i=0,n=fabric.document.documentElement,r=fabric.document.body||{scrollLeft:0,scrollTop:0};t&&(t.parentNode||t.host)&&(t=t.parentNode||t.host,t===fabric.document?(e=r.scrollLeft||n.scrollLeft||0,i=r.scrollTop||n.scrollTop||0):(e+=t.scrollLeft||0,i+=t.scrollTop||0),1!==t.nodeType||"fixed"!==t.style.position););return{left:e,top:i}}function a(t){var e,i,n=t&&t.ownerDocument,a={left:0,top:0},o={left:0,top:0},s={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!n)return o;for(var c in s)o[s[c]]+=parseInt(d(t,c),10)||0;return e=n.documentElement,"undefined"!=typeof t.getBoundingClientRect&&(a=t.getBoundingClientRect()),i=r(t),{left:a.left+i.left-(e.clientLeft||0)+o.left,top:a.top+i.top-(e.clientTop||0)+o.top}}function o(t){var e=fabric.jsdomImplForWrapper(t);return e._canvas||e._image}function s(t){if(fabric.isLikelyNode){var e=fabric.jsdomImplForWrapper(t);e&&(e._image=null,e._canvas=null,e._currentSrc=null,e._attributes=null,e._classList=null)}}function c(t,e){t.imageSmoothingEnabled=t.imageSmoothingEnabled||t.webkitImageSmoothingEnabled||t.mozImageSmoothingEnabled||t.msImageSmoothingEnabled||t.oImageSmoothingEnabled,t.imageSmoothingEnabled=e}var l,f=Array.prototype.slice,u=function(t){return f.call(t,0)};try{l=u(fabric.document.childNodes)instanceof Array}catch(h){}l||(u=function(t){for(var e=new Array(t.length),i=t.length;i--;)e[i]=t[i];return e});var d;d=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(t,e){var i=fabric.document.defaultView.getComputedStyle(t,null);return i?i[e]:void 0}:function(t,e){var i=t.style[e];return!i&&t.currentStyle&&(i=t.currentStyle[e]),i},function(){function t(t){return"undefined"!=typeof t.onselectstart&&(t.onselectstart=fabric.util.falseFunction),n?t.style[n]="none":"string"==typeof t.unselectable&&(t.unselectable="on"),t}function e(t){return"undefined"!=typeof t.onselectstart&&(t.onselectstart=null),n?t.style[n]="":"string"==typeof t.unselectable&&(t.unselectable=""),t}var i=fabric.document.documentElement.style,n="userSelect"in i?"userSelect":"MozUserSelect"in i?"MozUserSelect":"WebkitUserSelect"in i?"WebkitUserSelect":"KhtmlUserSelect"in i?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=t,fabric.util.makeElementSelectable=e}(),fabric.util.setImageSmoothing=c,fabric.util.getById=t,fabric.util.toArray=u,fabric.util.addClass=i,fabric.util.makeElement=e,fabric.util.wrapElement=n,fabric.util.getScrollLeftTop=r,fabric.util.getElementOffset=a,fabric.util.getNodeCanvas=o,fabric.util.cleanUpJsdomNode=s}();!function(){function t(t,e){return t+(/\?/.test(t)?"&":"?")+e}function e(){}function i(i,n){n||(n={});var r=n.method?n.method.toUpperCase():"GET",a=n.onComplete||function(){},o=new fabric.window.XMLHttpRequest,s=n.body||n.parameters;return o.onreadystatechange=function(){4===o.readyState&&(a(o),o.onreadystatechange=e)},"GET"===r&&(s=null,"string"==typeof n.parameters&&(i=t(i,n.parameters))),o.open(r,i,!0),("POST"===r||"PUT"===r)&&o.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),o.send(s),o}fabric.util.request=i}();fabric.log=console.log,fabric.warn=console.warn;!function(){function t(){return!1}function e(t,e,i,n){return-i*Math.cos(t/n*(Math.PI/2))+i+e}function i(i){i||(i={});var r,a=!1,c=function(){var t=fabric.runningAnimations.indexOf(r);return t>-1&&fabric.runningAnimations.splice(t,1)[0]};return r=s(o(i),{cancel:function(){return a=!0,c()},currentValue:"startValue"in i?i.startValue:0,completionRate:0,durationRate:0}),fabric.runningAnimations.push(r),n(function(s){var o,l=s||+new Date,h=i.duration||500,u=l+h,f=i.onChange||t,d=i.abort||t,p=i.onComplete||t,g=i.easing||e,m="startValue"in i?i.startValue.length>0:!1,v="startValue"in i?i.startValue:0,y="endValue"in i?i.endValue:100,b=i.byValue||(m?v.map(function(t,e){return y[e]-v[e]}):y-v);i.onStart&&i.onStart(),function x(t){o=t||+new Date;var e=o>u?h:o-l,i=e/h,s=m?v.map(function(t,i){return g(e,v[i],b[i],h)}):g(e,v,b,h),_=Math.abs(m?(s[0]-v[0])/b[0]:(s-v)/b);return r.currentValue=m?s.slice():s,r.completionRate=_,r.durationRate=i,a?void 0:d(s,_,i)?void c():o>u?(r.currentValue=m?y.slice():y,r.completionRate=1,r.durationRate=1,f(m?y.slice():y,1,1),p(y,1,1),void c()):(f(s,_,i),void n(x))}(l)}),r.cancel}function n(){return c.apply(fabric.window,arguments)}function r(){return l.apply(fabric.window,arguments)}var s=fabric.util.object.extend,o=fabric.util.object.clone,a=[];fabric.util.object.extend(a,{cancelAll:function(){var t=this.splice(0);return t.forEach(function(t){t.cancel()}),t},cancelByCanvas:function(t){if(!t)return[];var e=this.filter(function(e){return"object"==typeof e.target&&e.target.canvas===t});return e.forEach(function(t){t.cancel()}),e},cancelByTarget:function(t){var e=this.findAnimationsByTarget(t);return e.forEach(function(t){t.cancel()}),e},findAnimationIndex:function(t){return this.indexOf(this.findAnimation(t))},findAnimation:function(t){return this.find(function(e){return e.cancel===t})},findAnimationsByTarget:function(t){return t?this.filter(function(e){return e.target===t}):[]}});var c=fabric.window.requestAnimationFrame||fabric.window.webkitRequestAnimationFrame||fabric.window.mozRequestAnimationFrame||fabric.window.oRequestAnimationFrame||fabric.window.msRequestAnimationFrame||function(t){return fabric.window.setTimeout(t,1e3/60)},l=fabric.window.cancelAnimationFrame||fabric.window.clearTimeout;fabric.util.animate=i,fabric.util.requestAnimFrame=n,fabric.util.cancelAnimFrame=r,fabric.runningAnimations=a}();!function(){function t(t,e,i){var n="rgba("+parseInt(t[0]+i*(e[0]-t[0]),10)+","+parseInt(t[1]+i*(e[1]-t[1]),10)+","+parseInt(t[2]+i*(e[2]-t[2]),10);return n+=","+(t&&e?parseFloat(t[3]+i*(e[3]-t[3])):1),n+=")"}function e(e,i,n,r){var s=new fabric.Color(e).getSource(),o=new fabric.Color(i).getSource(),a=r.onComplete,c=r.onChange;return r=r||{},fabric.util.animate(fabric.util.object.extend(r,{duration:n||500,startValue:s,endValue:o,byValue:o,easing:function(e,i,n,s){var o=r.colorEasing?r.colorEasing(e,s):1-Math.cos(e/s*(Math.PI/2));return t(i,n,o)},onComplete:function(e,i,n){return a?a(t(o,o,0),i,n):void 0},onChange:function(e,i,n){if(c){if(Array.isArray(e))return c(t(e,e,0),i,n);c(e,i,n)}}}))}fabric.util.animateColor=e}();!function(){function t(t,e,i,n){return tt?i/2*t*t*t+e:i/2*((t-=2)*t*t+2)+e}function r(t,e,i,n){return i*(t/=n)*t*t*t+e}function s(t,e,i,n){return-i*((t=t/n-1)*t*t*t-1)+e}function o(t,e,i,n){return t/=n/2,1>t?i/2*t*t*t*t+e:-i/2*((t-=2)*t*t*t-2)+e}function a(t,e,i,n){return i*(t/=n)*t*t*t*t+e}function c(t,e,i,n){return i*((t=t/n-1)*t*t*t*t+1)+e}function l(t,e,i,n){return t/=n/2,1>t?i/2*t*t*t*t*t+e:i/2*((t-=2)*t*t*t*t+2)+e}function h(t,e,i,n){return-i*Math.cos(t/n*(Math.PI/2))+i+e}function u(t,e,i,n){return i*Math.sin(t/n*(Math.PI/2))+e}function f(t,e,i,n){return-i/2*(Math.cos(Math.PI*t/n)-1)+e}function d(t,e,i,n){return 0===t?e:i*Math.pow(2,10*(t/n-1))+e}function p(t,e,i,n){return t===n?e+i:i*(-Math.pow(2,-10*t/n)+1)+e}function g(t,e,i,n){return 0===t?e:t===n?e+i:(t/=n/2,1>t?i/2*Math.pow(2,10*(t-1))+e:i/2*(-Math.pow(2,-10*--t)+2)+e)}function m(t,e,i,n){return-i*(Math.sqrt(1-(t/=n)*t)-1)+e}function v(t,e,i,n){return i*Math.sqrt(1-(t=t/n-1)*t)+e}function y(t,e,i,n){return t/=n/2,1>t?-i/2*(Math.sqrt(1-t*t)-1)+e:i/2*(Math.sqrt(1-(t-=2)*t)+1)+e}function b(i,n,r,s){var o=1.70158,a=0,c=r;if(0===i)return n;if(i/=s,1===i)return n+r;a||(a=.3*s);var l=t(c,r,a,o);return-e(l,i,s)+n}function x(e,i,n,r){var s=1.70158,o=0,a=n;if(0===e)return i;if(e/=r,1===e)return i+n;o||(o=.3*r);var c=t(a,n,o,s);return c.a*Math.pow(2,-10*e)*Math.sin(2*(e*r-c.s)*Math.PI/c.p)+c.c+i}function _(i,n,r,s){var o=1.70158,a=0,c=r;if(0===i)return n;if(i/=s/2,2===i)return n+r;a||(a=.3*s*1.5);var l=t(c,r,a,o);return 1>i?-.5*e(l,i,s)+n:l.a*Math.pow(2,-10*(i-=1))*Math.sin(2*(i*s-l.s)*Math.PI/l.p)*.5+l.c+n}function S(t,e,i,n,r){return void 0===r&&(r=1.70158),i*(t/=n)*t*((r+1)*t-r)+e}function C(t,e,i,n,r){return void 0===r&&(r=1.70158),i*((t=t/n-1)*t*((r+1)*t+r)+1)+e}function T(t,e,i,n,r){return void 0===r&&(r=1.70158),t/=n/2,1>t?i/2*t*t*(((r*=1.525)+1)*t-r)+e:i/2*((t-=2)*t*(((r*=1.525)+1)*t+r)+2)+e}function w(t,e,i,n){return i-O(n-t,0,i,n)+e}function O(t,e,i,n){return(t/=n)<1/2.75?7.5625*i*t*t+e:2/2.75>t?i*(7.5625*(t-=1.5/2.75)*t+.75)+e:2.5/2.75>t?i*(7.5625*(t-=2.25/2.75)*t+.9375)+e:i*(7.5625*(t-=2.625/2.75)*t+.984375)+e}function M(t,e,i,n){return n/2>t?.5*w(2*t,0,i,n)+e:.5*O(2*t-n,0,i,n)+.5*i+e}fabric.util.ease={easeInQuad:function(t,e,i,n){return i*(t/=n)*t+e},easeOutQuad:function(t,e,i,n){return-i*(t/=n)*(t-2)+e},easeInOutQuad:function(t,e,i,n){return t/=n/2,1>t?i/2*t*t+e:-i/2*(--t*(t-2)-1)+e},easeInCubic:function(t,e,i,n){return i*(t/=n)*t*t+e},easeOutCubic:i,easeInOutCubic:n,easeInQuart:r,easeOutQuart:s,easeInOutQuart:o,easeInQuint:a,easeOutQuint:c,easeInOutQuint:l,easeInSine:h,easeOutSine:u,easeInOutSine:f,easeInExpo:d,easeOutExpo:p,easeInOutExpo:g,easeInCirc:m,easeOutCirc:v,easeInOutCirc:y,easeInElastic:b,easeOutElastic:x,easeInOutElastic:_,easeInBack:S,easeOutBack:C,easeInOutBack:T,easeInBounce:w,easeOutBounce:O,easeInOutBounce:M}}();!function(t){"use strict";function e(t){return t in E?E[t]:t}function i(t,e,i,n){var r,s=Array.isArray(e);if("fill"!==t&&"stroke"!==t||"none"!==e){if("strokeUniform"===t)return"non-scaling-stroke"===e;if("strokeDashArray"===t)e="none"===e?null:e.replace(/,/g," ").split(/\s+/).map(parseFloat);else if("transformMatrix"===t)e=i&&i.transformMatrix?S(i.transformMatrix,v.parseTransformAttribute(e)):v.parseTransformAttribute(e);else if("visible"===t)e="none"!==e&&"hidden"!==e,i&&i.visible===!1&&(e=!1);else if("opacity"===t)e=parseFloat(e),i&&"undefined"!=typeof i.opacity&&(e*=i.opacity);else if("textAnchor"===t)e="start"===e?"left":"end"===e?"right":"center";else if("charSpacing"===t)r=_(e,n)/n*1e3;else if("paintFirst"===t){var o=e.indexOf("fill"),a=e.indexOf("stroke"),e="fill";o>-1&&a>-1&&o>a?e="stroke":-1===o&&a>-1&&(e="stroke")}else{if("href"===t||"xlink:href"===t||"font"===t)return e;if("imageSmoothing"===t)return"optimizeQuality"===e;r=s?e.map(_):_(e,n)}}else e="";return!s&&isNaN(r)?e:r}function n(t){return new RegExp("^("+t.join("|")+")\\b","i")}function r(t){for(var e in M)if("undefined"!=typeof t[M[e]]&&""!==t[e]){if("undefined"==typeof t[e]){if(!v.Object.prototype[e])continue;t[e]=v.Object.prototype[e]}if(0!==t[e].indexOf("url(")){var i=new v.Color(t[e]);t[e]=i.setAlpha(x(i.getAlpha()*t[M[e]],2)).toRgba()}}return t}function s(t,e){var i,n,r,s,o=[];for(r=0,s=e.length;s>r;r++)i=e[r],n=t.getElementsByTagName(i),o=o.concat(Array.prototype.slice.call(n));return o}function o(t,e){var i,n;t.replace(/;\s*$/,"").split(";").forEach(function(t){var r=t.split(":");i=r[0].trim().toLowerCase(),n=r[1].trim(),e[i]=n})}function a(t,e){var i,n;for(var r in t)"undefined"!=typeof t[r]&&(i=r.toLowerCase(),n=t[r],e[i]=n)}function c(t,e){var i={};for(var n in v.cssRules[e])if(l(t,n.split(" ")))for(var r in v.cssRules[e][n])i[r]=v.cssRules[e][n][r];return i}function l(t,e){var i,n=!0;return i=u(t,e.pop()),i&&e.length&&(n=h(t,e)),i&&n&&0===e.length}function h(t,e){for(var i,n=!0;t.parentNode&&1===t.parentNode.nodeType&&e.length;)n&&(i=e.pop()),t=t.parentNode,n=u(t,i);return 0===e.length}function u(t,e){var i,n,r=t.nodeName,s=t.getAttribute("class"),o=t.getAttribute("id");if(i=new RegExp("^"+r,"i"),e=e.replace(i,""),o&&e.length&&(i=new RegExp("#"+o+"(?![a-zA-Z\\-]+)","i"),e=e.replace(i,"")),s&&e.length)for(s=s.split(" "),n=s.length;n--;)i=new RegExp("\\."+s[n]+"(?![a-zA-Z\\-]+)","i"),e=e.replace(i,"");return 0===e.length}function f(t,e){var i;if(t.getElementById&&(i=t.getElementById(e)),i)return i;var n,r,s,o=t.getElementsByTagName("*");for(r=0,s=o.length;s>r;r++)if(n=o[r],e===n.getAttribute("id"))return n}function d(t){for(var e=s(t,["use","svg:use"]),i=0;e.length&&ic;c++)a=l.item(c),_.setAttributeNS(x,a.nodeName,a.nodeValue);for(;m.firstChild;)_.appendChild(m.firstChild);m=_}for(c=0,l=n.attributes,h=l.length;h>c;c++)a=l.item(c),"x"!==a.nodeName&&"y"!==a.nodeName&&"xlink:href"!==a.nodeName&&"href"!==a.nodeName&&("transform"===a.nodeName?y=a.nodeValue+" "+y:m.setAttribute(a.nodeName,a.nodeValue));m.setAttribute("transform",y),m.setAttribute("instantiated_by_use","1"),m.removeAttribute("id"),o=n.parentNode,o.replaceChild(m,n),e.length===b&&i++}}function p(t){if(!v.svgViewBoxElementsRegEx.test(t.nodeName))return{};var e,i,n,r,s=t.getAttribute("viewBox"),o=1,a=1,c=0,l=0,h=t.getAttribute("width"),u=t.getAttribute("height"),f=t.getAttribute("x")||0,d=t.getAttribute("y")||0,p=t.getAttribute("preserveAspectRatio")||"",g=!s||!(s=s.match(L)),m=!h||!u||"100%"===h||"100%"===u,y=g&&m,b={},x="",S=0,C=0;if(b.width=0,b.height=0,b.toBeParsed=y,g&&(f||d)&&t.parentNode&&"#document"!==t.parentNode.nodeName&&(x=" translate("+_(f)+" "+_(d)+") ",n=(t.getAttribute("transform")||"")+x,t.setAttribute("transform",n),t.removeAttribute("x"),t.removeAttribute("y")),y)return b;if(g)return b.width=_(h),b.height=_(u),b;if(c=-parseFloat(s[1]),l=-parseFloat(s[2]),e=parseFloat(s[3]),i=parseFloat(s[4]),b.minX=c,b.minY=l,b.viewBoxWidth=e,b.viewBoxHeight=i,m?(b.width=e,b.height=i):(b.width=_(h),b.height=_(u),o=b.width/e,a=b.height/i),p=v.util.parsePreserveAspectRatioAttribute(p),"none"!==p.alignX&&("meet"===p.meetOrSlice&&(a=o=o>a?a:o),"slice"===p.meetOrSlice&&(a=o=o>a?o:a),S=b.width-e*o,C=b.height-i*o,"Mid"===p.alignX&&(S/=2),"Mid"===p.alignY&&(C/=2),"Min"===p.alignX&&(S=0),"Min"===p.alignY&&(C=0)),1===o&&1===a&&0===c&&0===l&&0===f&&0===d)return b;if((f||d)&&"#document"!==t.parentNode.nodeName&&(x=" translate("+_(f)+" "+_(d)+") "),n=x+" matrix("+o+" 0 0 "+a+" "+(c*o+S)+" "+(l*a+C)+") ","svg"===t.nodeName){for(r=t.ownerDocument.createElementNS(v.svgNS,"g");t.firstChild;)r.appendChild(t.firstChild);t.appendChild(r)}else r=t,r.removeAttribute("x"),r.removeAttribute("y"),n=r.getAttribute("transform")+n;return r.setAttribute("transform",n),b}function g(t,e){for(;t&&(t=t.parentNode);)if(t.nodeName&&e.test(t.nodeName.replace("svg:",""))&&!t.getAttribute("instantiated_by_use"))return!0;return!1}function m(t,e){var i=["gradientTransform","x1","x2","y1","y2","gradientUnits","cx","cy","r","fx","fy"],n="xlink:href",r=e.getAttribute(n).slice(1),s=f(t,r);if(s&&s.getAttribute(n)&&m(t,s),i.forEach(function(t){s&&!e.hasAttribute(t)&&s.hasAttribute(t)&&e.setAttribute(t,s.getAttribute(t))}),!e.children.length)for(var o=s.cloneNode(!0);o.firstChild;)e.appendChild(o.firstChild);e.removeAttribute(n)}var v=t.fabric||(t.fabric={}),y=v.util.object.extend,b=v.util.object.clone,x=v.util.toFixed,_=v.util.parseUnit,S=v.util.multiplyTransformMatrices,C=["path","circle","polygon","polyline","ellipse","rect","line","image","text"],w=["symbol","image","marker","pattern","view","svg"],T=["pattern","defs","symbol","metadata","clipPath","mask","desc"],O=["symbol","g","a","svg","clipPath","defs"],E={cx:"left",x:"left",r:"radius",cy:"top",y:"top",display:"visible",visibility:"visible",transform:"transformMatrix","fill-opacity":"fillOpacity","fill-rule":"fillRule","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","letter-spacing":"charSpacing","paint-order":"paintFirst","stroke-dasharray":"strokeDashArray","stroke-dashoffset":"strokeDashOffset","stroke-linecap":"strokeLineCap","stroke-linejoin":"strokeLineJoin","stroke-miterlimit":"strokeMiterLimit","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","text-decoration":"textDecoration","text-anchor":"textAnchor",opacity:"opacity","clip-path":"clipPath","clip-rule":"clipRule","vector-effect":"strokeUniform","image-rendering":"imageSmoothing"},M={stroke:"strokeOpacity",fill:"fillOpacity"},k="font-size",A="clip-path";v.svgValidTagNamesRegEx=n(C),v.svgViewBoxElementsRegEx=n(w),v.svgInvalidAncestorsRegEx=n(T),v.svgValidParentsRegEx=n(O),v.cssRules={},v.gradientDefs={},v.clipPaths={},v.parseTransformAttribute=function(){function t(t,e){var i=v.util.cos(e[0]),n=v.util.sin(e[0]),r=0,s=0;3===e.length&&(r=e[1],s=e[2]),t[0]=i,t[1]=n,t[2]=-n,t[3]=i,t[4]=r-(i*r-n*s),t[5]=s-(n*r+i*s)}function e(t,e){var i=e[0],n=2===e.length?e[1]:e[0];t[0]=i,t[3]=n}function i(t,e,i){t[i]=Math.tan(v.util.degreesToRadians(e[0]))}function n(t,e){t[4]=e[0],2===e.length&&(t[5]=e[1])}var r=v.iMatrix,s=v.reNum,o=v.commaWsp,a="(?:(skewX)\\s*\\(\\s*("+s+")\\s*\\))",c="(?:(skewY)\\s*\\(\\s*("+s+")\\s*\\))",l="(?:(rotate)\\s*\\(\\s*("+s+")(?:"+o+"("+s+")"+o+"("+s+"))?\\s*\\))",h="(?:(scale)\\s*\\(\\s*("+s+")(?:"+o+"("+s+"))?\\s*\\))",u="(?:(translate)\\s*\\(\\s*("+s+")(?:"+o+"("+s+"))?\\s*\\))",f="(?:(matrix)\\s*\\(\\s*("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")"+o+"("+s+")\\s*\\))",d="(?:"+f+"|"+u+"|"+h+"|"+l+"|"+a+"|"+c+")",p="(?:"+d+"(?:"+o+"*"+d+")*)",g="^\\s*(?:"+p+"?)\\s*$",m=new RegExp(g),y=new RegExp(d,"g");return function(s){var o=r.concat(),a=[];if(!s||s&&!m.test(s))return o;s.replace(y,function(s){var c=new RegExp(d).exec(s).filter(function(t){return!!t}),l=c[1],h=c.slice(2).map(parseFloat);switch(l){case"translate":n(o,h);break;case"rotate":h[0]=v.util.degreesToRadians(h[0]),t(o,h);break;case"scale":e(o,h);break;case"skewX":i(o,h,2);break;case"skewY":i(o,h,1);break;case"matrix":o=h}a.push(o.concat()),o=r.concat()});for(var c=a[0];a.length>1;)a.shift(),c=v.util.multiplyTransformMatrices(c,a[0]);return c}}();var L=new RegExp("^\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*,?\\s*("+v.reNum+"+)\\s*$");v.parseSVGDocument=function(t,e,i,n){if(t){d(t);var r,s,o=v.Object.__uid++,a=p(t),c=v.util.toArray(t.getElementsByTagName("*"));if(a.crossOrigin=n&&n.crossOrigin,a.svgUid=o,0===c.length&&v.isLikelyNode){c=t.selectNodes('//*[name(.)!="svg"]');var l=[];for(r=0,s=c.length;s>r;r++)l[r]=c[r];c=l}var h=c.filter(function(t){return p(t),v.svgValidTagNamesRegEx.test(t.nodeName.replace("svg:",""))&&!g(t,v.svgInvalidAncestorsRegEx)});if(!h||h&&!h.length)return void(e&&e([],{}));var u={};c.filter(function(t){return"clipPath"===t.nodeName.replace("svg:","")}).forEach(function(t){var e=t.getAttribute("id");u[e]=v.util.toArray(t.getElementsByTagName("*")).filter(function(t){return v.svgValidTagNamesRegEx.test(t.nodeName.replace("svg:",""))})}),v.gradientDefs[o]=v.getGradientDefs(t),v.cssRules[o]=v.getCSSRules(t),v.clipPaths[o]=u,v.parseElements(h,function(t,i){e&&(e(t,a,i,c),delete v.gradientDefs[o],delete v.cssRules[o],delete v.clipPaths[o])},b(a),i,n)}};var I=new RegExp("(normal|italic)?\\s*(normal|small-caps)?\\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*("+v.reNum+"(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|"+v.reNum+"))?\\s+(.*)");y(v,{parseFontDeclaration:function(t,e){var i=t.match(I);if(i){var n=i[1],r=i[3],s=i[4],o=i[5],a=i[6];n&&(e.fontStyle=n),r&&(e.fontWeight=isNaN(parseFloat(r))?r:parseFloat(r)),s&&(e.fontSize=_(s)),a&&(e.fontFamily=a),o&&(e.lineHeight="normal"===o?1:o)}},getGradientDefs:function(t){var e,i=["linearGradient","radialGradient","svg:linearGradient","svg:radialGradient"],n=s(t,i),r=0,o={};for(r=n.length;r--;)e=n[r],e.getAttribute("xlink:href")&&m(t,e),o[e.getAttribute("id")]=e;return o},parseAttributes:function(t,n,s){if(t){var o,a,l,h={};"undefined"==typeof s&&(s=t.getAttribute("svgUid")),t.parentNode&&v.svgValidParentsRegEx.test(t.parentNode.nodeName)&&(h=v.parseAttributes(t.parentNode,n,s));var u=n.reduce(function(e,i){return o=t.getAttribute(i),o&&(e[i]=o),e},{}),f=y(c(t,s),v.parseStyleAttribute(t));u=y(u,f),f[A]&&t.setAttribute(A,f[A]),a=l=h.fontSize||v.Text.DEFAULT_SVG_FONT_SIZE,u[k]&&(u[k]=a=_(u[k],l));var d,p,g={};for(var m in u)d=e(m),p=i(d,u[m],h,a),g[d]=p;g&&g.font&&v.parseFontDeclaration(g.font,g);var b=y(h,g);return v.svgValidParentsRegEx.test(t.nodeName)?b:r(b)}},parseElements:function(t,e,i,n,r){new v.ElementsParser(t,e,i,n,r).parse()},parseStyleAttribute:function(t){var e={},i=t.getAttribute("style");return i?("string"==typeof i?o(i,e):a(i,e),e):e},parsePointsAttribute:function(t){if(!t)return null;t=t.replace(/,/g," ").trim(),t=t.split(/\s+/);var e,i,n=[];for(e=0,i=t.length;i>e;e+=2)n.push({x:parseFloat(t[e]),y:parseFloat(t[e+1])});return n},getCSSRules:function(t){var e,i,n,r=t.getElementsByTagName("style"),s={};for(e=0,i=r.length;i>e;e++){var o=r[e].textContent;o=o.replace(/\/\*[\s\S]*?\*\//g,""),""!==o.trim()&&(n=o.split("}"),n=n.filter(function(t){return t.trim()}),n.forEach(function(t){var n=t.split("{"),r={},o=n[1].trim(),a=o.split(";").filter(function(t){return t.trim()});for(e=0,i=a.length;i>e;e++){var c=a[e].split(":"),l=c[0].trim(),h=c[1].trim();r[l]=h}t=n[0].trim(),t.split(",").forEach(function(t){t=t.replace(/^svg/i,"").trim(),""!==t&&(s[t]?v.util.object.extend(s[t],r):s[t]=v.util.object.clone(r))})}))}return s},loadSVGFromURL:function(t,e,i,n){function r(t){var r=t.responseXML;return r&&r.documentElement?void v.parseSVGDocument(r.documentElement,function(t,i,n,r){e&&e(t,i,n,r)},i,n):(e&&e(null),!1)}t=t.replace(/^\n\s*/,"").trim(),new v.util.request(t,{method:"get",onComplete:r})},loadSVGFromString:function(t,e,i,n){var r=new v.window.DOMParser,s=r.parseFromString(t.trim(),"text/xml");v.parseSVGDocument(s.documentElement,function(t,i,n,r){e(t,i,n,r)},i,n)}})}("undefined"!=typeof exports?exports:this);fabric.ElementsParser=function(t,e,i,n,r,s){this.elements=t,this.callback=e,this.options=i,this.reviver=n,this.svgUid=i&&i.svgUid||0,this.parsingOptions=r,this.regexUrl=/^url\(['"]?#([^'"]+)['"]?\)/g,this.doc=s},function(t){t.parse=function(){this.instances=new Array(this.elements.length),this.numElements=this.elements.length,this.createObjects()},t.createObjects=function(){var t=this;this.elements.forEach(function(e,i){e.setAttribute("svgUid",t.svgUid),t.createObject(e,i)})},t.findTag=function(t){return fabric[fabric.util.string.capitalize(t.tagName.replace("svg:",""))]},t.createObject=function(t,e){var i=this.findTag(t);if(i&&i.fromElement)try{i.fromElement(t,this.createCallback(e,t),this.options)}catch(n){fabric.log(n)}else this.checkIfDone()},t.createCallback=function(t,e){var i=this;return function(n){var r;i.resolveGradient(n,e,"fill"),i.resolveGradient(n,e,"stroke"),n instanceof fabric.Image&&n._originalElement&&(r=n.parsePreserveAspectRatioAttribute(e)),n._removeTransformMatrix(r),i.resolveClipPath(n,e),i.reviver&&i.reviver(e,n),i.instances[t]=n,i.checkIfDone()}},t.extractPropertyDefinition=function(t,e,i){var n=t[e],r=this.regexUrl;if(r.test(n)){r.lastIndex=0;var s=r.exec(n)[1];return r.lastIndex=0,fabric[i][this.svgUid][s]}},t.resolveGradient=function(t,e,i){var n=this.extractPropertyDefinition(t,i,"gradientDefs");if(n){var r=e.getAttribute(i+"-opacity"),s=fabric.Gradient.fromElement(n,t,r,this.options);t.set(i,s)}},t.createClipPathCallback=function(t,e){return function(t){t._removeTransformMatrix(),t.fillRule=t.clipRule,e.push(t)}},t.resolveClipPath=function(t,e){var i,n,r,s,a,o,c=this.extractPropertyDefinition(t,"clipPath","clipPaths");if(c){s=[],r=fabric.util.invertTransform(t.calcTransformMatrix());for(var l=c[0].parentNode,h=e;h.parentNode&&h.getAttribute("clip-path")!==t.clipPath;)h=h.parentNode;h.parentNode.appendChild(l);for(var f=0;ft.x&&this.y>t.y},gte:function(t){return this.x>=t.x&&this.y>=t.y},lerp:function(t,i){return"undefined"==typeof i&&(i=.5),i=Math.max(Math.min(1,i),0),new e(this.x+(t.x-this.x)*i,this.y+(t.y-this.y)*i)},distanceFrom:function(t){var e=this.x-t.x,i=this.y-t.y;return Math.sqrt(e*e+i*i)},midPointFrom:function(t){return this.lerp(t)},min:function(t){return new e(Math.min(this.x,t.x),Math.min(this.y,t.y))},max:function(t){return new e(Math.max(this.x,t.x),Math.max(this.y,t.y))},toString:function(){return this.x+","+this.y},setXY:function(t,e){return this.x=t,this.y=e,this},setX:function(t){return this.x=t,this},setY:function(t){return this.y=t,this},setFromPoint:function(t){return this.x=t.x,this.y=t.y,this},swap:function(t){var e=this.x,i=this.y;this.x=t.x,this.y=t.y,t.x=e,t.y=i},clone:function(){return new e(this.x,this.y)}}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t){this.status=t,this.points=[]}var i=t.fabric||(t.fabric={});return i.Intersection?void i.warn("fabric.Intersection is already defined"):(i.Intersection=e,i.Intersection.prototype={constructor:e,appendPoint:function(t){return this.points.push(t),this},appendPoints:function(t){return this.points=this.points.concat(t),this}},i.Intersection.intersectLineLine=function(t,n,r,s){var a,o=(s.x-r.x)*(t.y-r.y)-(s.y-r.y)*(t.x-r.x),c=(n.x-t.x)*(t.y-r.y)-(n.y-t.y)*(t.x-r.x),l=(s.y-r.y)*(n.x-t.x)-(s.x-r.x)*(n.y-t.y);if(0!==l){var h=o/l,f=c/l;h>=0&&1>=h&&f>=0&&1>=f?(a=new e("Intersection"),a.appendPoint(new i.Point(t.x+h*(n.x-t.x),t.y+h*(n.y-t.y)))):a=new e}else a=new e(0===o||0===c?"Coincident":"Parallel");return a},i.Intersection.intersectLinePolygon=function(t,i,n){var r,s,a,o,c=new e,l=n.length;for(o=0;l>o;o++)r=n[o],s=n[(o+1)%l],a=e.intersectLineLine(t,i,r,s),c.appendPoints(a.points);return c.points.length>0&&(c.status="Intersection"),c},i.Intersection.intersectPolygonPolygon=function(t,i){var n,r=new e,s=t.length;for(n=0;s>n;n++){var a=t[n],o=t[(n+1)%s],c=e.intersectLinePolygon(a,o,i);r.appendPoints(c.points)}return r.points.length>0&&(r.status="Intersection"),r},void(i.Intersection.intersectPolygonRectangle=function(t,n,r){var s=n.min(r),a=n.max(r),o=new i.Point(a.x,s.y),c=new i.Point(s.x,a.y),l=e.intersectLinePolygon(s,o,t),h=e.intersectLinePolygon(o,a,t),f=e.intersectLinePolygon(a,c,t),u=e.intersectLinePolygon(c,s,t),d=new e;return d.appendPoints(l.points),d.appendPoints(h.points),d.appendPoints(f.points),d.appendPoints(u.points),d.points.length>0&&(d.status="Intersection"),d}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t){t?this._tryParsingColor(t):this.setSource([0,0,0,1])}function i(t,e,i){return 0>i&&(i+=1),i>1&&(i-=1),1/6>i?t+6*(e-t)*i:.5>i?e:2/3>i?t+(e-t)*(2/3-i)*6:t}var n=t.fabric||(t.fabric={});return n.Color?void n.warn("fabric.Color is already defined."):(n.Color=e,n.Color.prototype={_tryParsingColor:function(t){var i;t in e.colorNameMap&&(t=e.colorNameMap[t]),"transparent"===t&&(i=[255,255,255,0]),i||(i=e.sourceFromHex(t)),i||(i=e.sourceFromRgb(t)),i||(i=e.sourceFromHsl(t)),i||(i=[0,0,0,1]),i&&this.setSource(i)},_rgbToHsl:function(t,e,i){t/=255,e/=255,i/=255;var r,s,a,o=n.util.array.max([t,e,i]),l=n.util.array.min([t,e,i]);if(a=(o+l)/2,o===l)r=s=0;else{var c=o-l;switch(s=a>.5?c/(2-o-l):c/(o+l),o){case t:r=(e-i)/c+(i>e?6:0);break;case e:r=(i-t)/c+2;break;case i:r=(t-e)/c+4}r/=6}return[Math.round(360*r),Math.round(100*s),Math.round(100*a)]},getSource:function(){return this._source},setSource:function(t){this._source=t},toRgb:function(){var t=this.getSource();return"rgb("+t[0]+","+t[1]+","+t[2]+")"},toRgba:function(){var t=this.getSource();return"rgba("+t[0]+","+t[1]+","+t[2]+","+t[3]+")"},toHsl:function(){var t=this.getSource(),e=this._rgbToHsl(t[0],t[1],t[2]);return"hsl("+e[0]+","+e[1]+"%,"+e[2]+"%)"},toHsla:function(){var t=this.getSource(),e=this._rgbToHsl(t[0],t[1],t[2]);return"hsla("+e[0]+","+e[1]+"%,"+e[2]+"%,"+t[3]+")"},toHex:function(){var t,e,i,n=this.getSource();return t=n[0].toString(16),t=1===t.length?"0"+t:t,e=n[1].toString(16),e=1===e.length?"0"+e:e,i=n[2].toString(16),i=1===i.length?"0"+i:i,t.toUpperCase()+e.toUpperCase()+i.toUpperCase()},toHexa:function(){var t,e=this.getSource();return t=Math.round(255*e[3]),t=t.toString(16),t=1===t.length?"0"+t:t,this.toHex()+t.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(t){var e=this.getSource();return e[3]=t,this.setSource(e),this},toGrayscale:function(){var t=this.getSource(),e=parseInt((.3*t[0]+.59*t[1]+.11*t[2]).toFixed(0),10),i=t[3];return this.setSource([e,e,e,i]),this},toBlackWhite:function(t){var e=this.getSource(),i=(.3*e[0]+.59*e[1]+.11*e[2]).toFixed(0),n=e[3];return t=t||127,i=Number(i)i;i++)n.push(Math.round(a[i]*(1-s)+o[i]*s));return n[3]=r,this.setSource(n),this}},n.Color.reRGBa=/^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*((?:\d*\.?\d+)?)\s*)?\)$/i,n.Color.reHSLa=/^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/i,n.Color.reHex=/^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i,n.Color.colorNameMap={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aqua:"#00FFFF",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blue:"#0000FF",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",darkgreen:"#006400",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",fuchsia:"#FF00FF",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",lightgreen:"#90EE90",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",lime:"#00FF00",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",maroon:"#800000",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",navy:"#000080",oldlace:"#FDF5E6",olive:"#808000",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",purple:"#800080",rebeccapurple:"#663399",red:"#FF0000",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",silver:"#C0C0C0",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",teal:"#008080",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",white:"#FFFFFF",whitesmoke:"#F5F5F5",yellow:"#FFFF00",yellowgreen:"#9ACD32"},n.Color.fromRgb=function(t){return e.fromSource(e.sourceFromRgb(t))},n.Color.sourceFromRgb=function(t){var i=t.match(e.reRGBa);if(i){var n=parseInt(i[1],10)/(/%$/.test(i[1])?100:1)*(/%$/.test(i[1])?255:1),r=parseInt(i[2],10)/(/%$/.test(i[2])?100:1)*(/%$/.test(i[2])?255:1),s=parseInt(i[3],10)/(/%$/.test(i[3])?100:1)*(/%$/.test(i[3])?255:1);return[parseInt(n,10),parseInt(r,10),parseInt(s,10),i[4]?parseFloat(i[4]):1]}},n.Color.fromRgba=e.fromRgb,n.Color.fromHsl=function(t){return e.fromSource(e.sourceFromHsl(t))},n.Color.sourceFromHsl=function(t){var n=t.match(e.reHSLa);if(n){var r,s,a,o=(parseFloat(n[1])%360+360)%360/360,l=parseFloat(n[2])/(/%$/.test(n[2])?100:1),c=parseFloat(n[3])/(/%$/.test(n[3])?100:1);if(0===l)r=s=a=c;else{var h=.5>=c?c*(l+1):c+l-c*l,u=2*c-h;r=i(u,h,o+1/3),s=i(u,h,o),a=i(u,h,o-1/3)}return[Math.round(255*r),Math.round(255*s),Math.round(255*a),n[4]?parseFloat(n[4]):1]}},n.Color.fromHsla=e.fromHsl,n.Color.fromHex=function(t){return e.fromSource(e.sourceFromHex(t))},n.Color.sourceFromHex=function(t){if(t.match(e.reHex)){var i=t.slice(t.indexOf("#")+1),n=3===i.length||4===i.length,r=8===i.length||4===i.length,s=n?i.charAt(0)+i.charAt(0):i.substring(0,2),a=n?i.charAt(1)+i.charAt(1):i.substring(2,4),o=n?i.charAt(2)+i.charAt(2):i.substring(4,6),l=r?n?i.charAt(3)+i.charAt(3):i.substring(6,8):"FF";return[parseInt(s,16),parseInt(a,16),parseInt(o,16),parseFloat((parseInt(l,16)/255).toFixed(2))]}},void(n.Color.fromSource=function(t){var i=new e;return i.setSource(t),i}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t,e){var i=t.angle+R(Math.atan2(e.y,e.x))+360;return Math.round(i%360/45)}function i(t,e){var i=e.transform.target,n=i.canvas,r=E.util.object.clone(e);r.target=i,n&&n.fire("object:"+t,r),i.fire(t,e)}function n(t,e){var i=e.canvas,n=i.uniScaleKey,r=t[n];return i.uniformScaling&&!r||!i.uniformScaling&&r}function r(t){return t.originX===j&&t.originY===j}function s(t,e,i){var n=t.lockScalingX,r=t.lockScalingY;return n&&r?!0:!e&&(n||r)&&i?!0:n&&"x"===e?!0:r&&"y"===e?!0:!1}function a(t,i,r){var a="not-allowed",o=n(t,r),l="";if(0!==i.x&&0===i.y?l="x":0===i.x&&0!==i.y&&(l="y"),s(r,l,o))return a;var c=e(r,i);return D[c]+"-resize"}function o(t,i,n){var r="not-allowed";if(0!==i.x&&n.lockSkewingY)return r;if(0!==i.y&&n.lockSkewingX)return r;var s=e(n,i)%4;return M[s]+"-resize"}function l(t,e,i){return t[i.canvas.altActionKey]?L.skewCursorStyleHandler(t,e,i):L.scaleCursorStyleHandler(t,e,i)}function c(t,e,i){var n=t[i.canvas.altActionKey];return 0===e.x?n?"skewX":"scaleY":0===e.y?n?"skewY":"scaleX":void 0}function h(t,e,i){return i.lockRotation?"not-allowed":e.cursorStyle}function u(t,e,i,n){return{e:t,transform:e,pointer:{x:i,y:n}}}function f(t){return function(e,i,n,r){var s=i.target,a=s.getCenterPoint(),o=s.translateToOriginPoint(a,i.originX,i.originY),l=t(e,i,n,r);return s.setPositionByOrigin(o,i.originX,i.originY),l}}function d(t,e){return function(n,r,s,a){var o=e(n,r,s,a);return o&&i(t,u(n,r,s,a)),o}}function g(t,e,i,n,r){var s=t.target,a=s.controls[t.corner],o=s.canvas.getZoom(),l=s.padding/o,c=s.toLocalPoint(new E.Point(n,r),e,i);return c.x>=l&&(c.x-=l),c.x<=-l&&(c.x+=l),c.y>=l&&(c.y-=l),c.y<=l&&(c.y+=l),c.x-=a.offsetX,c.y-=a.offsetY,c}function p(t){return t.flipX!==t.flipY}function m(t,e,i,n,r){if(0!==t[e]){var s=t._getTransformedDimensions()[n],a=r/s*t[i];t.set(i,a)}}function y(t,e,i,n){var r,s=e.target,a=s._getTransformedDimensions(0,s.skewY),o=g(e,e.originX,e.originY,i,n),l=Math.abs(2*o.x)-a.x,c=s.skewX;2>l?r=0:(r=R(Math.atan2(l/s.scaleX,a.y/s.scaleY)),e.originX===P&&e.originY===H&&(r=-r),e.originX===B&&e.originY===I&&(r=-r),p(s)&&(r=-r));var h=c!==r;if(h){var u=s._getTransformedDimensions().y;s.set("skewX",r),m(s,"skewY","scaleY","y",u)}return h}function v(t,e,i,n){var r,s=e.target,a=s._getTransformedDimensions(s.skewX,0),o=g(e,e.originX,e.originY,i,n),l=Math.abs(2*o.y)-a.y,c=s.skewY;2>l?r=0:(r=R(Math.atan2(l/s.scaleY,a.x/s.scaleX)),e.originX===P&&e.originY===H&&(r=-r),e.originX===B&&e.originY===I&&(r=-r),p(s)&&(r=-r));var h=c!==r;if(h){var u=s._getTransformedDimensions().x;s.set("skewY",r),m(s,"skewX","scaleX","x",u)}return h}function x(t,e,i,n){var r,s=e.target,a=s.skewX,o=e.originY;if(s.lockSkewingX)return!1;if(0===a){var l=g(e,j,j,i,n);r=l.x>0?P:B}else a>0&&(r=o===I?P:B),0>a&&(r=o===I?B:P),p(s)&&(r=r===P?B:P);e.originX=r;var c=d("skewing",f(y));return c(t,e,i,n)}function b(t,e,i,n){var r,s=e.target,a=s.skewY,o=e.originX;if(s.lockSkewingY)return!1;if(0===a){var l=g(e,j,j,i,n);r=l.y>0?I:H}else a>0&&(r=o===P?I:H),0>a&&(r=o===P?H:I),p(s)&&(r=r===I?H:I);e.originY=r;var c=d("skewing",f(v));return c(t,e,i,n)}function _(t,e,i,n){var r=e,s=r.target,a=s.translateToOriginPoint(s.getCenterPoint(),r.originX,r.originY);if(s.lockRotation)return!1;var o=Math.atan2(r.ey-a.y,r.ex-a.x),l=Math.atan2(n-a.y,i-a.x),c=R(l-o+r.theta),h=!0;if(s.snapAngle>0){var u=s.snapAngle,f=s.snapThreshold||u,d=Math.ceil(c/u)*u,g=Math.floor(c/u)*u;Math.abs(c-g)c&&(c=360+c),c%=360,h=s.angle!==c,s.angle=c,h}function S(t,e,i,a,o){o=o||{};var l,c,h,u,f,d,p=e.target,m=p.lockScalingX,y=p.lockScalingY,v=o.by,x=n(t,p),b=s(p,v,x),_=e.gestureScale;if(b)return!1;if(_)c=e.scaleX*_,h=e.scaleY*_;else{if(l=g(e,e.originX,e.originY,i,a),f="y"!==v?N(l.x):1,d="x"!==v?N(l.y):1,e.signX||(e.signX=f),e.signY||(e.signY=d),p.lockScalingFlip&&(e.signX!==f||e.signY!==d))return!1;if(u=p._getTransformedDimensions(),x&&!v){var S=Math.abs(l.x)+Math.abs(l.y),C=e.original,w=Math.abs(u.x*C.scaleX/p.scaleX)+Math.abs(u.y*C.scaleY/p.scaleY),T=S/w;c=C.scaleX*T,h=C.scaleY*T}else c=Math.abs(l.x*p.scaleX/u.x),h=Math.abs(l.y*p.scaleY/u.y);r(e)&&(c*=2,h*=2),e.signX!==f&&"y"!==v&&(e.originX=W[e.originX],c*=-1,e.signX=f),e.signY!==d&&"x"!==v&&(e.originY=W[e.originY],h*=-1,e.signY=d)}var F=p.scaleX,k=p.scaleY;return v?("x"===v&&p.set("scaleX",c),"y"===v&&p.set("scaleY",h)):(!m&&p.set("scaleX",c),!y&&p.set("scaleY",h)),F!==p.scaleX||k!==p.scaleY}function C(t,e,i,n){return S(t,e,i,n)}function w(t,e,i,n){return S(t,e,i,n,{by:"x"})}function T(t,e,i,n){return S(t,e,i,n,{by:"y"})}function F(t,e,i,n){return t[e.target.canvas.altActionKey]?L.skewHandlerX(t,e,i,n):L.scalingY(t,e,i,n)}function k(t,e,i,n){return t[e.target.canvas.altActionKey]?L.skewHandlerY(t,e,i,n):L.scalingX(t,e,i,n)}function O(t,e,i,n){var s=e.target,a=g(e,e.originX,e.originY,i,n),o=s.strokeWidth/(s.strokeUniform?s.scaleX:1),l=r(e)?2:1,c=s.width,h=Math.abs(a.x*l/s.scaleX)-o;return s.set("width",Math.max(h,0)),c!==h}function A(t,e,n,r){var s=e.target,a=n-e.offsetX,o=r-e.offsetY,l=!s.get("lockMovementX")&&s.left!==a,c=!s.get("lockMovementY")&&s.top!==o;return l&&s.set("left",a),c&&s.set("top",o),(l||c)&&i("moving",u(t,e,n,r)),l||c}var E=t.fabric||(t.fabric={}),D=["e","se","s","sw","w","nw","n","ne","e"],M=["ns","nesw","ew","nwse"],L={},P="left",I="top",B="right",H="bottom",j="center",W={top:H,bottom:I,left:B,right:P,center:j},R=E.util.radiansToDegrees,N=Math.sign||function(t){return(t>0)-(0>t)||+t};L.scaleCursorStyleHandler=a,L.skewCursorStyleHandler=o,L.scaleSkewCursorStyleHandler=l,L.rotationWithSnapping=d("rotating",f(_)),L.scalingEqually=d("scaling",f(C)),L.scalingX=d("scaling",f(w)),L.scalingY=d("scaling",f(T)),L.scalingYOrSkewingX=F,L.scalingXOrSkewingY=k,L.changeWidth=d("resizing",f(O)),L.skewHandlerX=x,L.skewHandlerY=b,L.dragHandler=A,L.scaleOrSkewActionName=c,L.rotationStyleHandler=h,L.fireEvent=i,L.wrapWithFixedAnchor=f,L.wrapWithFireEvent=d,L.getLocalPoint=g,E.controlsUtils=L}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t,e,i,n,r){n=n||{};var s,a=this.sizeX||n.cornerSize||r.cornerSize,o=this.sizeY||n.cornerSize||r.cornerSize,c="undefined"!=typeof n.transparentCorners?n.transparentCorners:r.transparentCorners,l=c?"stroke":"fill",h=!c&&(n.cornerStrokeColor||r.cornerStrokeColor),u=e,f=i;t.save(),t.fillStyle=n.cornerColor||r.cornerColor,t.strokeStyle=n.cornerStrokeColor||r.cornerStrokeColor,a>o?(s=a,t.scale(1,o/a),f=i*a/o):o>a?(s=o,t.scale(a/o,1),u=e*o/a):s=a,t.lineWidth=1,t.beginPath(),t.arc(u,f,s/2,0,2*Math.PI,!1),t[l](),h&&t.stroke(),t.restore()}function i(t,e,i,n,s){n=n||{};var a=this.sizeX||n.cornerSize||s.cornerSize,o=this.sizeY||n.cornerSize||s.cornerSize,c="undefined"!=typeof n.transparentCorners?n.transparentCorners:s.transparentCorners,l=c?"stroke":"fill",h=!c&&(n.cornerStrokeColor||s.cornerStrokeColor),u=a/2,f=o/2;t.save(),t.fillStyle=n.cornerColor||s.cornerColor,t.strokeStyle=n.cornerStrokeColor||s.cornerStrokeColor,t.lineWidth=1,t.translate(e,i),t.rotate(r(s.angle)),t[l+"Rect"](-u,-f,a,o),h&&t.strokeRect(-u,-f,a,o),t.restore()}var n=t.fabric||(t.fabric={}),r=n.util.degreesToRadians,s=n.controlsUtils;s.renderCircleControl=e,s.renderSquareControl=i}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t){for(var e in t)this[e]=t[e]}var i=t.fabric||(t.fabric={});i.Control=e,i.Control.prototype={visible:!0,actionName:"scale",angle:0,x:0,y:0,offsetX:0,offsetY:0,sizeX:null,sizeY:null,touchSizeX:null,touchSizeY:null,cursorStyle:"crosshair",withConnection:!1,actionHandler:function(){},mouseDownHandler:function(){},mouseUpHandler:function(){},getActionHandler:function(){return this.actionHandler},getMouseDownHandler:function(){return this.mouseDownHandler},getMouseUpHandler:function(){return this.mouseUpHandler},cursorStyleHandler:function(t,e){return e.cursorStyle},getActionName:function(t,e){return e.actionName},getVisibility:function(t,e){var i=t._controlsVisibility;return i&&"undefined"!=typeof i[e]?i[e]:this.visible},setVisibility:function(t){this.visible=t},positionHandler:function(t,e){var n=i.util.transformPoint({x:this.x*t.x+this.offsetX,y:this.y*t.y+this.offsetY},e);return n},calcCornerCoords:function(t,e,n,r,s){var a,o,c,l,h=s?this.touchSizeX:this.sizeX,u=s?this.touchSizeY:this.sizeY;if(h&&u&&h!==u){var f=Math.atan2(u,h),d=Math.sqrt(h*h+u*u)/2,g=f-i.util.degreesToRadians(t),p=Math.PI/2-f-i.util.degreesToRadians(t);a=d*i.util.cos(g),o=d*i.util.sin(g),c=d*i.util.cos(p),l=d*i.util.sin(p)}else{var m=h&&u?h:e;d=.7071067812*m;var g=i.util.degreesToRadians(45-t);a=c=d*i.util.cos(g),o=l=d*i.util.sin(g)}return{tl:{x:n-l,y:r-c},tr:{x:n+a,y:r-o},bl:{x:n-a,y:r+o},br:{x:n+l,y:r+c}}},render:function(t,e,n,r,s){switch(r=r||{},r.cornerStyle||s.cornerStyle){case"circle":i.controlsUtils.renderCircleControl.call(this,t,e,n,r,s);break;default:i.controlsUtils.renderSquareControl.call(this,t,e,n,r,s)}}}}("undefined"!=typeof exports?exports:this);!function(){function t(t,e){var i,r,n,s,o=t.getAttribute("style"),a=t.getAttribute("offset")||0;if(a=parseFloat(a)/(/%$/.test(a)?100:1),a=0>a?0:a>1?1:a,o){var l=o.split(/\s*;\s*/);for(""===l[l.length-1]&&l.pop(),s=l.length;s--;){var c=l[s].split(/\s*:\s*/),h=c[0].trim(),u=c[1].trim();"stop-color"===h?i=u:"stop-opacity"===h&&(n=u)}}return i||(i=t.getAttribute("stop-color")||"rgb(0,0,0)"),n||(n=t.getAttribute("stop-opacity")),i=new fabric.Color(i),r=i.getAlpha(),n=isNaN(parseFloat(n))?1:parseFloat(n),n*=r*e,{offset:a,color:i.toRgb(),opacity:n}}function e(t){return{x1:t.getAttribute("x1")||0,y1:t.getAttribute("y1")||0,x2:t.getAttribute("x2")||"100%",y2:t.getAttribute("y2")||0}}function i(t){return{x1:t.getAttribute("fx")||t.getAttribute("cx")||"50%",y1:t.getAttribute("fy")||t.getAttribute("cy")||"50%",r1:0,x2:t.getAttribute("cx")||"50%",y2:t.getAttribute("cy")||"50%",r2:t.getAttribute("r")||"50%"}}function r(t,e,i,r){var n,s;Object.keys(e).forEach(function(t){n=e[t],"Infinity"===n?s=1:"-Infinity"===n?s=0:(s=parseFloat(e[t],10),"string"==typeof n&&/^(\d+\.\d+)%|(\d+)%$/.test(n)&&(s*=.01,"pixels"===r&&(("x1"===t||"x2"===t||"r2"===t)&&(s*=i.viewBoxWidth||i.width),("y1"===t||"y2"===t)&&(s*=i.viewBoxHeight||i.height)))),e[t]=s})}var n=fabric.util.object.clone;fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,gradientTransform:null,gradientUnits:"pixels",type:"linear",initialize:function(t){t||(t={}),t.coords||(t.coords={});var e,i=this;Object.keys(t).forEach(function(e){i[e]=t[e]}),this.id?this.id+="_"+fabric.Object.__uid++:this.id=fabric.Object.__uid++,e={x1:t.coords.x1||0,y1:t.coords.y1||0,x2:t.coords.x2||0,y2:t.coords.y2||0},"radial"===this.type&&(e.r1=t.coords.r1||0,e.r2=t.coords.r2||0),this.coords=e,this.colorStops=t.colorStops.slice()},addColorStop:function(t){for(var e in t){var i=new fabric.Color(t[e]);this.colorStops.push({offset:parseFloat(e),color:i.toRgb(),opacity:i.getAlpha()})}return this},toObject:function(t){var e={type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY,gradientUnits:this.gradientUnits,gradientTransform:this.gradientTransform?this.gradientTransform.concat():this.gradientTransform};return fabric.util.populateWithProperties(this,e,t),e},toSVG:function(t,e){var i,r,s,o,a=n(this.coords,!0),e=e||{},l=n(this.colorStops,!0),c=a.r1>a.r2,h=this.gradientTransform?this.gradientTransform.concat():fabric.iMatrix.concat(),u=-this.offsetX,f=-this.offsetY,d=!!e.additionalTransform,g="pixels"===this.gradientUnits?"userSpaceOnUse":"objectBoundingBox";if(l.sort(function(t,e){return t.offset-e.offset}),"objectBoundingBox"===g?(u/=t.width,f/=t.height):(u+=t.width/2,f+=t.height/2),"path"===t.type&&"percentage"!==this.gradientUnits&&(u-=t.pathOffset.x,f-=t.pathOffset.y),h[4]-=u,h[5]-=f,o='id="SVGID_'+this.id+'" gradientUnits="'+g+'"',o+=' gradientTransform="'+(d?e.additionalTransform+" ":"")+fabric.util.matrixToSVG(h)+'" ',"linear"===this.type?s=["\n']:"radial"===this.type&&(s=["\n']),"radial"===this.type){if(c)for(l=l.concat(),l.reverse(),i=0,r=l.length;r>i;i++)l[i].offset=1-l[i].offset;var p=Math.min(a.r1,a.r2);if(p>0){var m=Math.max(a.r1,a.r2),y=p/m;for(i=0,r=l.length;r>i;i++)l[i].offset+=y*(1-l[i].offset)}}for(i=0,r=l.length;r>i;i++){var v=l[i];s.push("\n')}return s.push("linear"===this.type?"\n":"\n"),s.join("")},toLive:function(t){var e,i,r,n=fabric.util.object.clone(this.coords);if(this.type){for("linear"===this.type?e=t.createLinearGradient(n.x1,n.y1,n.x2,n.y2):"radial"===this.type&&(e=t.createRadialGradient(n.x1,n.y1,n.r1,n.x2,n.y2,n.r2)),i=0,r=this.colorStops.length;r>i;i++){var s=this.colorStops[i].color,o=this.colorStops[i].opacity,a=this.colorStops[i].offset;"undefined"!=typeof o&&(s=new fabric.Color(s).setAlpha(o).toRgba()),e.addColorStop(a,s)}return e}}}),fabric.util.object.extend(fabric.Gradient,{fromElement:function(n,s,o,a){var l=parseFloat(o)/(/%$/.test(o)?100:1);l=0>l?0:l>1?1:l,isNaN(l)&&(l=1);var c,h,u,f,d=n.getElementsByTagName("stop"),g="userSpaceOnUse"===n.getAttribute("gradientUnits")?"pixels":"percentage",p=n.getAttribute("gradientTransform")||"",m=[],y=0,v=0;for("linearGradient"===n.nodeName||"LINEARGRADIENT"===n.nodeName?(c="linear",h=e(n)):(c="radial",h=i(n)),u=d.length;u--;)m.push(t(d[u],l));f=fabric.parseTransformAttribute(p),r(s,h,a,g),"pixels"===g&&(y=-s.left,v=-s.top);var x=new fabric.Gradient({id:n.getAttribute("id"),type:c,coords:h,colorStops:m,gradientUnits:g,gradientTransform:f,offsetX:y,offsetY:v});return x}})}();!function(){"use strict";var t=fabric.util.toFixed;fabric.Pattern=fabric.util.createClass({repeat:"repeat",offsetX:0,offsetY:0,crossOrigin:"",patternTransform:null,initialize:function(t,e){if(t||(t={}),this.id=fabric.Object.__uid++,this.setOptions(t),!t.source||t.source&&"string"!=typeof t.source)return void(e&&e(this));var i=this;this.source=fabric.util.createImage(),fabric.util.loadImage(t.source,function(t,r){i.source=t,e&&e(i,r)},null,this.crossOrigin)},toObject:function(e){var i,r,n=fabric.Object.NUM_FRACTION_DIGITS;return"string"==typeof this.source.src?i=this.source.src:"object"==typeof this.source&&this.source.toDataURL&&(i=this.source.toDataURL()),r={type:"pattern",source:i,repeat:this.repeat,crossOrigin:this.crossOrigin,offsetX:t(this.offsetX,n),offsetY:t(this.offsetY,n),patternTransform:this.patternTransform?this.patternTransform.concat():null},fabric.util.populateWithProperties(this,r,e),r},toSVG:function(t){var e="function"==typeof this.source?this.source():this.source,i=e.width/t.width,r=e.height/t.height,n=this.offsetX/t.width,s=this.offsetY/t.height,o="";return("repeat-x"===this.repeat||"no-repeat"===this.repeat)&&(r=1,s&&(r+=Math.abs(s))),("repeat-y"===this.repeat||"no-repeat"===this.repeat)&&(i=1,n&&(i+=Math.abs(n))),e.src?o=e.src:e.toDataURL&&(o=e.toDataURL()),'\n\n\n'},setOptions:function(t){for(var e in t)this[e]=t[e]},toLive:function(t){var e=this.source;if(!e)return"";if("undefined"!=typeof e.src){if(!e.complete)return"";if(0===e.naturalWidth||0===e.naturalHeight)return""}return t.createPattern(e,this.repeat)}})}();!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.toFixed;return e.Shadow?void e.warn("fabric.Shadow is already defined."):(e.Shadow=e.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,nonScaling:!1,initialize:function(t){"string"==typeof t&&(t=this._parseShadow(t));for(var i in t)this[i]=t[i];this.id=e.Object.__uid++},_parseShadow:function(t){var i=t.trim(),r=e.Shadow.reOffsetsAndBlur.exec(i)||[],n=i.replace(e.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:n.trim(),offsetX:parseFloat(r[1],10)||0,offsetY:parseFloat(r[2],10)||0,blur:parseFloat(r[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(t){var r=40,n=40,s=e.Object.NUM_FRACTION_DIGITS,o=e.util.rotateVector({x:this.offsetX,y:this.offsetY},e.util.degreesToRadians(-t.angle)),a=20,l=new e.Color(this.color);return t.width&&t.height&&(r=100*i((Math.abs(o.x)+this.blur)/t.width,s)+a,n=100*i((Math.abs(o.y)+this.blur)/t.height,s)+a),t.flipX&&(o.x*=-1),t.flipY&&(o.y*=-1),'\n \n \n \n \n \n \n \n \n\n'},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY,affectStroke:this.affectStroke,nonScaling:this.nonScaling};var t={},i=e.Shadow.prototype;return["color","blur","offsetX","offsetY","affectStroke","nonScaling"].forEach(function(e){this[e]!==i[e]&&(t[e]=this[e])},this),t}}),void(e.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:\.\d*)?(?:px)?(?:\s?|$))?(-?\d+(?:\.\d*)?(?:px)?(?:\s?|$))?(\d+(?:\.\d*)?(?:px)?)?(?:\s?|$)(?:$|\s)/))}("undefined"!=typeof exports?exports:this);!function(){"use strict";if(fabric.StaticCanvas)return void fabric.warn("fabric.StaticCanvas is already defined.");var t=fabric.util.object.extend,e=fabric.util.getElementOffset,i=fabric.util.removeFromArray,r=fabric.util.toFixed,n=fabric.util.transformPoint,s=fabric.util.invertTransform,o=fabric.util.getNodeCanvas,a=fabric.util.createCanvasElement,c=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass(fabric.CommonMethods,{initialize:function(t,e){e||(e={}),this.renderAndResetBound=this.renderAndReset.bind(this),this.requestRenderAllBound=this.requestRenderAll.bind(this),this._initStatic(t,e)},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!1,renderOnAddRemove:!0,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,viewportTransform:fabric.iMatrix.concat(),backgroundVpt:!0,overlayVpt:!0,enableRetinaScaling:!0,vptCoords:{},skipOffscreen:!0,clipPath:void 0,_initStatic:function(t,e){var i=this.requestRenderAllBound;this._objects=[],this._createLowerCanvas(t),this._initOptions(e),this.interactive||this._initRetinaScaling(),e.overlayImage&&this.setOverlayImage(e.overlayImage,i),e.backgroundImage&&this.setBackgroundImage(e.backgroundImage,i),e.backgroundColor&&this.setBackgroundColor(e.backgroundColor,i),e.overlayColor&&this.setOverlayColor(e.overlayColor,i),this.calcOffset()},_isRetinaScaling:function(){return fabric.devicePixelRatio>1&&this.enableRetinaScaling},getRetinaScaling:function(){return this._isRetinaScaling()?Math.max(1,fabric.devicePixelRatio):1},_initRetinaScaling:function(){if(this._isRetinaScaling()){var t=fabric.devicePixelRatio;this.__initRetinaScaling(t,this.lowerCanvasEl,this.contextContainer),this.upperCanvasEl&&this.__initRetinaScaling(t,this.upperCanvasEl,this.contextTop)}},__initRetinaScaling:function(t,e,i){e.setAttribute("width",this.width*t),e.setAttribute("height",this.height*t),i.scale(t,t)},calcOffset:function(){return this._offset=e(this.lowerCanvasEl),this},setOverlayImage:function(t,e,i){return this.__setBgOverlayImage("overlayImage",t,e,i)},setBackgroundImage:function(t,e,i){return this.__setBgOverlayImage("backgroundImage",t,e,i)},setOverlayColor:function(t,e){return this.__setBgOverlayColor("overlayColor",t,e)},setBackgroundColor:function(t,e){return this.__setBgOverlayColor("backgroundColor",t,e)},__setBgOverlayImage:function(t,e,i,r){return"string"==typeof e?fabric.util.loadImage(e,function(e,n){if(e){var s=new fabric.Image(e,r);this[t]=s,s.canvas=this}i&&i(e,n)},this,r&&r.crossOrigin):(r&&e.setOptions(r),this[t]=e,e&&(e.canvas=this),i&&i(e,!1)),this},__setBgOverlayColor:function(t,e,i){return this[t]=e,this._initGradient(e,t),this._initPattern(e,t,i),this},_createCanvasElement:function(){var t=a();if(!t)throw c;if(t.style||(t.style={}),"undefined"==typeof t.getContext)throw c;return t},_initOptions:function(t){var e=this.lowerCanvasEl;this._setOptions(t),this.width=this.width||parseInt(e.width,10)||0,this.height=this.height||parseInt(e.height,10)||0,this.lowerCanvasEl.style&&(e.width=this.width,e.height=this.height,e.style.width=this.width+"px",e.style.height=this.height+"px",this.viewportTransform=this.viewportTransform.slice())},_createLowerCanvas:function(t){this.lowerCanvasEl=t&&t.getContext?t:fabric.util.getById(t)||this._createCanvasElement(),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this._originalCanvasStyle=this.lowerCanvasEl.style,this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(t,e){return this.setDimensions({width:t},e)},setHeight:function(t,e){return this.setDimensions({height:t},e)},setDimensions:function(t,e){var i;e=e||{};for(var r in t)i=t[r],e.cssOnly||(this._setBackstoreDimension(r,t[r]),i+="px",this.hasLostContext=!0),e.backstoreOnly||this._setCssDimension(r,i);return this._isCurrentlyDrawing&&this.freeDrawingBrush&&this.freeDrawingBrush._setBrushStyles(this.contextTop),this._initRetinaScaling(),this.calcOffset(),e.cssOnly||this.requestRenderAll(),this},_setBackstoreDimension:function(t,e){return this.lowerCanvasEl[t]=e,this.upperCanvasEl&&(this.upperCanvasEl[t]=e),this.cacheCanvasEl&&(this.cacheCanvasEl[t]=e),this[t]=e,this},_setCssDimension:function(t,e){return this.lowerCanvasEl.style[t]=e,this.upperCanvasEl&&(this.upperCanvasEl.style[t]=e),this.wrapperEl&&(this.wrapperEl.style[t]=e),this},getZoom:function(){return this.viewportTransform[0]},setViewportTransform:function(t){var e,i,r,n=this._activeObject,s=this.backgroundImage,o=this.overlayImage;for(this.viewportTransform=t,i=0,r=this._objects.length;r>i;i++)e=this._objects[i],e.group||e.setCoords(!0);return n&&n.setCoords(),s&&s.setCoords(!0),o&&o.setCoords(!0),this.calcViewportBoundaries(),this.renderOnAddRemove&&this.requestRenderAll(),this},zoomToPoint:function(t,e){var i=t,r=this.viewportTransform.slice(0);t=n(t,s(this.viewportTransform)),r[0]=e,r[3]=e;var o=n(t,r);return r[4]+=i.x-o.x,r[5]+=i.y-o.y,this.setViewportTransform(r)},setZoom:function(t){return this.zoomToPoint(new fabric.Point(0,0),t),this},absolutePan:function(t){var e=this.viewportTransform.slice(0);return e[4]=-t.x,e[5]=-t.y,this.setViewportTransform(e)},relativePan:function(t){return this.absolutePan(new fabric.Point(-t.x-this.viewportTransform[4],-t.y-this.viewportTransform[5]))},getElement:function(){return this.lowerCanvasEl},_onObjectAdded:function(t){this.stateful&&t.setupState(),t._set("canvas",this),t.setCoords(),this.fire("object:added",{target:t}),t.fire("added")},_onObjectRemoved:function(t){this.fire("object:removed",{target:t}),t.fire("removed"),delete t.canvas},clearContext:function(t){return t.clearRect(0,0,this.width,this.height),this},getContext:function(){return this.contextContainer},clear:function(){return this.remove.apply(this,this.getObjects()),this.backgroundImage=null,this.overlayImage=null,this.backgroundColor="",this.overlayColor="",this._hasITextHandlers&&(this.off("mouse:up",this._mouseUpITextHandler),this._iTextInstances=null,this._hasITextHandlers=!1),this.clearContext(this.contextContainer),this.fire("canvas:cleared"),this.renderOnAddRemove&&this.requestRenderAll(),this},renderAll:function(){var t=this.contextContainer;return this.renderCanvas(t,this._objects),this},renderAndReset:function(){this.isRendering=0,this.renderAll()},requestRenderAll:function(){return this.isRendering||(this.isRendering=fabric.util.requestAnimFrame(this.renderAndResetBound)),this},calcViewportBoundaries:function(){var t={},e=this.width,i=this.height,r=s(this.viewportTransform);return t.tl=n({x:0,y:0},r),t.br=n({x:e,y:i},r),t.tr=new fabric.Point(t.br.x,t.tl.y),t.bl=new fabric.Point(t.tl.x,t.br.y),this.vptCoords=t,t},cancelRequestedRender:function(){this.isRendering&&(fabric.util.cancelAnimFrame(this.isRendering),this.isRendering=0)},renderCanvas:function(t,e){var i=this.viewportTransform,r=this.clipPath;this.cancelRequestedRender(),this.calcViewportBoundaries(),this.clearContext(t),fabric.util.setImageSmoothing(t,this.imageSmoothingEnabled),this.fire("before:render",{ctx:t}),this._renderBackground(t),t.save(),t.transform(i[0],i[1],i[2],i[3],i[4],i[5]),this._renderObjects(t,e),t.restore(),!this.controlsAboveOverlay&&this.interactive&&this.drawControls(t),r&&(r.canvas=this,r.shouldCache(),r._transformDone=!0,r.renderCache({forClipping:!0}),this.drawClipPathOnCanvas(t)),this._renderOverlay(t),this.controlsAboveOverlay&&this.interactive&&this.drawControls(t),this.fire("after:render",{ctx:t})},drawClipPathOnCanvas:function(t){var e=this.viewportTransform,i=this.clipPath;t.save(),t.transform(e[0],e[1],e[2],e[3],e[4],e[5]),t.globalCompositeOperation="destination-in",i.transform(t),t.scale(1/i.zoomX,1/i.zoomY),t.drawImage(i._cacheCanvas,-i.cacheTranslationX,-i.cacheTranslationY),t.restore()},_renderObjects:function(t,e){var i,r;for(i=0,r=e.length;r>i;++i)e[i]&&e[i].render(t)},_renderBackgroundOrOverlay:function(t,e){var i=this[e+"Color"],r=this[e+"Image"],n=this.viewportTransform,s=this[e+"Vpt"];if(i||r){if(i){t.save(),t.beginPath(),t.moveTo(0,0),t.lineTo(this.width,0),t.lineTo(this.width,this.height),t.lineTo(0,this.height),t.closePath(),t.fillStyle=i.toLive?i.toLive(t,this):i,s&&t.transform(n[0],n[1],n[2],n[3],n[4],n[5]),t.transform(1,0,0,1,i.offsetX||0,i.offsetY||0);var o=i.gradientTransform||i.patternTransform;o&&t.transform(o[0],o[1],o[2],o[3],o[4],o[5]),t.fill(),t.restore()}r&&(t.save(),s&&t.transform(n[0],n[1],n[2],n[3],n[4],n[5]),r.render(t),t.restore())}},_renderBackground:function(t){this._renderBackgroundOrOverlay(t,"background")},_renderOverlay:function(t){this._renderBackgroundOrOverlay(t,"overlay")},getCenter:function(){return{top:this.height/2,left:this.width/2}},getCenterPoint:function(){return new fabric.Point(this.width/2,this.height/2)},centerObjectH:function(t){return this._centerObject(t,new fabric.Point(this.getCenterPoint().x,t.getCenterPoint().y))},centerObjectV:function(t){return this._centerObject(t,new fabric.Point(t.getCenterPoint().x,this.getCenterPoint().y))},centerObject:function(t){var e=this.getCenterPoint();return this._centerObject(t,e)},viewportCenterObject:function(t){var e=this.getVpCenter();return this._centerObject(t,e)},viewportCenterObjectH:function(t){var e=this.getVpCenter();return this._centerObject(t,new fabric.Point(e.x,t.getCenterPoint().y)),this},viewportCenterObjectV:function(t){var e=this.getVpCenter();return this._centerObject(t,new fabric.Point(t.getCenterPoint().x,e.y))},getVpCenter:function(){var t=this.getCenterPoint(),e=s(this.viewportTransform);return n(t,e)},_centerObject:function(t,e){return t.setPositionByOrigin(e,"center","center"),t.setCoords(),this.renderOnAddRemove&&this.requestRenderAll(),this},toDatalessJSON:function(t){return this.toDatalessObject(t)},toObject:function(t){return this._toObjectMethod("toObject",t)},toDatalessObject:function(t){return this._toObjectMethod("toDatalessObject",t)},_toObjectMethod:function(e,i){var r=this.clipPath,n={version:fabric.version,objects:this._toObjects(e,i)};return r&&!r.excludeFromExport&&(n.clipPath=this._toObject(this.clipPath,e,i)),t(n,this.__serializeBgOverlay(e,i)),fabric.util.populateWithProperties(this,n,i),n},_toObjects:function(t,e){return this._objects.filter(function(t){return!t.excludeFromExport}).map(function(i){return this._toObject(i,t,e)},this)},_toObject:function(t,e,i){var r;this.includeDefaultValues||(r=t.includeDefaultValues,t.includeDefaultValues=!1);var n=t[e](i);return this.includeDefaultValues||(t.includeDefaultValues=r),n},__serializeBgOverlay:function(t,e){var i={},r=this.backgroundImage,n=this.overlayImage,s=this.backgroundColor,o=this.overlayColor;return s&&s.toObject?s.excludeFromExport||(i.background=s.toObject(e)):s&&(i.background=s),o&&o.toObject?o.excludeFromExport||(i.overlay=o.toObject(e)):o&&(i.overlay=o),r&&!r.excludeFromExport&&(i.backgroundImage=this._toObject(r,t,e)),n&&!n.excludeFromExport&&(i.overlayImage=this._toObject(n,t,e)),i},svgViewportTransformation:!0,toSVG:function(t,e){t||(t={}),t.reviver=e;var i=[];return this._setSVGPreamble(i,t),this._setSVGHeader(i,t),this.clipPath&&i.push('\n'),this._setSVGBgOverlayColor(i,"background"),this._setSVGBgOverlayImage(i,"backgroundImage",e),this._setSVGObjects(i,e),this.clipPath&&i.push("\n"),this._setSVGBgOverlayColor(i,"overlay"),this._setSVGBgOverlayImage(i,"overlayImage",e),i.push(""),i.join("")},_setSVGPreamble:function(t,e){e.suppressPreamble||t.push('\n','\n')},_setSVGHeader:function(t,e){var i,n=e.width||this.width,s=e.height||this.height,o='viewBox="0 0 '+this.width+" "+this.height+'" ',a=fabric.Object.NUM_FRACTION_DIGITS;e.viewBox?o='viewBox="'+e.viewBox.x+" "+e.viewBox.y+" "+e.viewBox.width+" "+e.viewBox.height+'" ':this.svgViewportTransformation&&(i=this.viewportTransform,o='viewBox="'+r(-i[4]/i[0],a)+" "+r(-i[5]/i[3],a)+" "+r(this.width/i[0],a)+" "+r(this.height/i[3],a)+'" '),t.push("\n',"Created with Fabric.js ",fabric.version,"\n","\n",this.createSVGFontFacesMarkup(),this.createSVGRefElementsMarkup(),this.createSVGClipPathMarkup(e),"\n")},createSVGClipPathMarkup:function(t){var e=this.clipPath;return e?(e.clipPathId="CLIPPATH_"+fabric.Object.__uid++,'\n'+this.clipPath.toClipPathSVG(t.reviver)+"\n"):""},createSVGRefElementsMarkup:function(){var t=this,e=["background","overlay"].map(function(e){var i=t[e+"Color"];if(i&&i.toLive){var r=t[e+"Vpt"],n=t.viewportTransform,s={width:t.width/(r?n[0]:1),height:t.height/(r?n[3]:1)};return i.toSVG(s,{additionalTransform:r?fabric.util.matrixToSVG(n):""})}});return e.join("")},createSVGFontFacesMarkup:function(){var t,e,i,r,n,s,o,a,c,l="",h={},u=fabric.fontPaths,f=[];for(this._objects.forEach(function g(t){f.push(t),t._objects&&t._objects.forEach(g)}),a=0,c=f.length;c>a;a++)if(t=f[a],e=t.fontFamily,-1!==t.type.indexOf("text")&&!h[e]&&u[e]&&(h[e]=!0,t.styles)){i=t.styles;for(n in i){r=i[n];for(o in r)s=r[o],e=s.fontFamily,!h[e]&&u[e]&&(h[e]=!0)}}for(var d in h)l+=[" @font-face {\n"," font-family: '",d,"';\n"," src: url('",u[d],"');\n"," }\n"].join("");return l&&(l=[' \n"].join("")),l},_setSVGObjects:function(t,e){var i,r,n,s=this._objects;for(r=0,n=s.length;n>r;r++)i=s[r],i.excludeFromExport||this._setSVGObject(t,i,e)},_setSVGObject:function(t,e,i){t.push(e.toSVG(i))},_setSVGBgOverlayImage:function(t,e,i){this[e]&&!this[e].excludeFromExport&&this[e].toSVG&&t.push(this[e].toSVG(i))},_setSVGBgOverlayColor:function(t,e){var i=this[e+"Color"],r=this.viewportTransform,n=this.width,s=this.height;if(i)if(i.toLive){var o=i.repeat,a=fabric.util.invertTransform(r),c=this[e+"Vpt"],l=c?fabric.util.matrixToSVG(a):"";t.push('\n")}else t.push('\n")},sendToBack:function(t){if(!t)return this;var e,r,n,s=this._activeObject;if(t===s&&"activeSelection"===t.type)for(n=s._objects,e=n.length;e--;)r=n[e],i(this._objects,r),this._objects.unshift(r);else i(this._objects,t),this._objects.unshift(t);return this.renderOnAddRemove&&this.requestRenderAll(),this},bringToFront:function(t){if(!t)return this;var e,r,n,s=this._activeObject;if(t===s&&"activeSelection"===t.type)for(n=s._objects,e=0;e0+l&&(o=s-1,i(this._objects,n),this._objects.splice(o,0,n)),l++;else s=this._objects.indexOf(t),0!==s&&(o=this._findNewLowerIndex(t,s,e),i(this._objects,t),this._objects.splice(o,0,t));return this.renderOnAddRemove&&this.requestRenderAll(),this},_findNewLowerIndex:function(t,e,i){var r,n;if(i)for(r=e,n=e-1;n>=0;--n){var s=t.intersectsWithObject(this._objects[n])||t.isContainedWithinObject(this._objects[n])||this._objects[n].isContainedWithinObject(t);if(s){r=n;break}}else r=e-1;return r},bringForward:function(t,e){if(!t)return this;var r,n,s,o,a,c=this._activeObject,l=0;if(t===c&&"activeSelection"===t.type)for(a=c._objects,r=a.length;r--;)n=a[r],s=this._objects.indexOf(n),sn;++n){var o=t.intersectsWithObject(this._objects[n])||t.isContainedWithinObject(this._objects[n])||this._objects[n].isContainedWithinObject(t);if(o){r=n;break}}else r=e+1;return r},moveTo:function(t,e){return i(this._objects,t),this._objects.splice(e,0,t),this.renderOnAddRemove&&this.requestRenderAll()},dispose:function(){return this.isRendering&&(fabric.util.cancelAnimFrame(this.isRendering),this.isRendering=0),this.forEachObject(function(t){t.dispose&&t.dispose()}),this._objects=[],this.backgroundImage&&this.backgroundImage.dispose&&this.backgroundImage.dispose(),this.backgroundImage=null,this.overlayImage&&this.overlayImage.dispose&&this.overlayImage.dispose(),this.overlayImage=null,this._iTextInstances=null,this.contextContainer=null,this.lowerCanvasEl.classList.remove("lower-canvas"),fabric.util.setStyle(this.lowerCanvasEl,this._originalCanvasStyle),delete this._originalCanvasStyle,this.lowerCanvasEl.setAttribute("width",this.width),this.lowerCanvasEl.setAttribute("height",this.height),fabric.util.cleanUpJsdomNode(this.lowerCanvasEl),this.lowerCanvasEl=void 0,this},toString:function(){return"#"}}),t(fabric.StaticCanvas.prototype,fabric.Observable),t(fabric.StaticCanvas.prototype,fabric.Collection),t(fabric.StaticCanvas.prototype,fabric.DataURLExporter),t(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(t){var e=a();if(!e||!e.getContext)return null;var i=e.getContext("2d");if(!i)return null;switch(t){case"setLineDash":return"undefined"!=typeof i.setLineDash;default:return null}}}),fabric.StaticCanvas.prototype.toJSON=fabric.StaticCanvas.prototype.toObject,fabric.isLikelyNode&&(fabric.StaticCanvas.prototype.createPNGStream=function(){var t=o(this.lowerCanvasEl);return t&&t.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(t){var e=o(this.lowerCanvasEl);return e&&e.createJPEGStream(t)})}();fabric.BaseBrush=fabric.util.createClass({color:"rgb(0, 0, 0)",width:1,shadow:null,strokeLineCap:"round",strokeLineJoin:"round",strokeMiterLimit:10,strokeDashArray:null,limitedToCanvasSize:!1,_setBrushStyles:function(t){t.strokeStyle=this.color,t.lineWidth=this.width,t.lineCap=this.strokeLineCap,t.miterLimit=this.strokeMiterLimit,t.lineJoin=this.strokeLineJoin,t.setLineDash(this.strokeDashArray||[])},_saveAndTransform:function(t){var e=this.canvas.viewportTransform;t.save(),t.transform(e[0],e[1],e[2],e[3],e[4],e[5])},_setShadow:function(){if(this.shadow){var t=this.canvas,e=this.shadow,i=t.contextTop,r=t.getZoom();t&&t._isRetinaScaling()&&(r*=fabric.devicePixelRatio),i.shadowColor=e.color,i.shadowBlur=e.blur*r,i.shadowOffsetX=e.offsetX*r,i.shadowOffsetY=e.offsetY*r}},needsFullRender:function(){var t=new fabric.Color(this.color);return t.getAlpha()<1||!!this.shadow},_resetShadow:function(){var t=this.canvas.contextTop;t.shadowColor="",t.shadowBlur=t.shadowOffsetX=t.shadowOffsetY=0},_isOutSideCanvas:function(t){return t.x<0||t.x>this.canvas.getWidth()||t.y<0||t.y>this.canvas.getHeight()}});!function(){fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{decimate:.4,drawStraightLine:!1,straightLineKey:"shiftKey",initialize:function(t){this.canvas=t,this._points=[]},needsFullRender:function(){return this.callSuper("needsFullRender")||this._hasStraightLine},_drawSegment:function(t,e,i){var r=e.midPointFrom(i);return t.quadraticCurveTo(e.x,e.y,r.x,r.y),r},onMouseDown:function(t,e){this.canvas._isMainEvent(e.e)&&(this.drawStraightLine=e.e[this.straightLineKey],this._prepareForDrawing(t),this._captureDrawingPath(t),this._render())},onMouseMove:function(t,e){if(this.canvas._isMainEvent(e.e)&&(this.drawStraightLine=e.e[this.straightLineKey],(this.limitedToCanvasSize!==!0||!this._isOutSideCanvas(t))&&this._captureDrawingPath(t)&&this._points.length>1))if(this.needsFullRender())this.canvas.clearContext(this.canvas.contextTop),this._render();else{var i=this._points,r=i.length,n=this.canvas.contextTop;this._saveAndTransform(n),this.oldEnd&&(n.beginPath(),n.moveTo(this.oldEnd.x,this.oldEnd.y)),this.oldEnd=this._drawSegment(n,i[r-2],i[r-1],!0),n.stroke(),n.restore()}},onMouseUp:function(t){return this.canvas._isMainEvent(t.e)?(this.drawStraightLine=!1,this.oldEnd=void 0,this._finalizeAndAddPath(),!1):!0},_prepareForDrawing:function(t){var e=new fabric.Point(t.x,t.y);this._reset(),this._addPoint(e),this.canvas.contextTop.moveTo(e.x,e.y)},_addPoint:function(t){return this._points.length>1&&t.eq(this._points[this._points.length-1])?!1:(this.drawStraightLine&&this._points.length>1&&(this._hasStraightLine=!0,this._points.pop()),this._points.push(t),!0)},_reset:function(){this._points=[],this._setBrushStyles(this.canvas.contextTop),this._setShadow(),this._hasStraightLine=!1},_captureDrawingPath:function(t){var e=new fabric.Point(t.x,t.y);return this._addPoint(e)},_render:function(t){var e,i,r=this._points[0],n=this._points[1];if(t=t||this.canvas.contextTop,this._saveAndTransform(t),t.beginPath(),2===this._points.length&&r.x===n.x&&r.y===n.y){var s=this.width/1e3;r=new fabric.Point(r.x,r.y),n=new fabric.Point(n.x,n.y),r.x-=s,n.x+=s}for(t.moveTo(r.x,r.y),e=1,i=this._points.length;i>e;e++)this._drawSegment(t,r,n),r=this._points[e],n=this._points[e+1];t.lineTo(r.x,r.y),t.stroke(),t.restore()},convertPointsToSVGPath:function(t){var e=this.width/1e3;return fabric.util.getSmoothPathFromPoints(t,e)},_isEmptySVGPath:function(t){var e=fabric.util.joinPath(t);return"M 0 0 Q 0 0 0 0 L 0 0"===e},createPath:function(t){var e=new fabric.Path(t,{fill:null,stroke:this.color,strokeWidth:this.width,strokeLineCap:this.strokeLineCap,strokeMiterLimit:this.strokeMiterLimit,strokeLineJoin:this.strokeLineJoin,strokeDashArray:this.strokeDashArray});return this.shadow&&(this.shadow.affectStroke=!0,e.shadow=new fabric.Shadow(this.shadow)),e},decimatePoints:function(t,e){if(t.length<=2)return t;var i,r,n=this.canvas.getZoom(),s=Math.pow(e/n,2),o=t.length-1,a=t[0],c=[a];for(i=1;o-1>i;i++)r=Math.pow(a.x-t[i].x,2)+Math.pow(a.y-t[i].y,2),r>=s&&(a=t[i],c.push(a));return c.push(t[o]),c},_finalizeAndAddPath:function(){var t=this.canvas.contextTop;t.closePath(),this.decimate&&(this._points=this.decimatePoints(this._points,this.decimate));var e=this.convertPointsToSVGPath(this._points);if(this._isEmptySVGPath(e))return void this.canvas.requestRenderAll();var i=this.createPath(e);this.canvas.clearContext(this.canvas.contextTop),this.canvas.fire("before:path:created",{path:i}),this.canvas.add(i),this.canvas.requestRenderAll(),i.setCoords(),this._resetShadow(),this.canvas.fire("path:created",{path:i})}})}();fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,initialize:function(t){this.canvas=t,this.points=[]},drawDot:function(t){var e=this.addPoint(t),i=this.canvas.contextTop;this._saveAndTransform(i),this.dot(i,e),i.restore()},dot:function(t,e){t.fillStyle=e.fill,t.beginPath(),t.arc(e.x,e.y,e.radius,0,2*Math.PI,!1),t.closePath(),t.fill()},onMouseDown:function(t){this.points.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.drawDot(t)},_render:function(){var t,e,i=this.canvas.contextTop,r=this.points;for(this._saveAndTransform(i),t=0,e=r.length;e>t;t++)this.dot(i,r[t]);i.restore()},onMouseMove:function(t){this.limitedToCanvasSize===!0&&this._isOutSideCanvas(t)||(this.needsFullRender()?(this.canvas.clearContext(this.canvas.contextTop),this.addPoint(t),this._render()):this.drawDot(t))},onMouseUp:function(){var t,e,i=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;var r=[];for(t=0,e=this.points.length;e>t;t++){var n=this.points[t],s=new fabric.Circle({radius:n.radius,left:n.x,top:n.y,originX:"center",originY:"center",fill:n.fill});this.shadow&&(s.shadow=new fabric.Shadow(this.shadow)),r.push(s)}var o=new fabric.Group(r);o.canvas=this.canvas,this.canvas.fire("before:path:created",{path:o}),this.canvas.add(o),this.canvas.fire("path:created",{path:o}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=i,this.canvas.requestRenderAll()},addPoint:function(t){var e=new fabric.Point(t.x,t.y),i=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,r=new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return e.radius=i,e.fill=r,this.points.push(e),e}});fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(t){this.canvas=t,this.sprayChunks=[]},onMouseDown:function(t){this.sprayChunks.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.addSprayChunk(t),this.render(this.sprayChunkPoints)},onMouseMove:function(t){this.limitedToCanvasSize===!0&&this._isOutSideCanvas(t)||(this.addSprayChunk(t),this.render(this.sprayChunkPoints))},onMouseUp:function(){var t=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var e=[],i=0,r=this.sprayChunks.length;r>i;i++)for(var n=this.sprayChunks[i],o=0,s=n.length;s>o;o++){var a=new fabric.Rect({width:n[o].width,height:n[o].width,left:n[o].x+1,top:n[o].y+1,originX:"center",originY:"center",fill:this.color});e.push(a)}this.optimizeOverlapping&&(e=this._getOptimizedRects(e));var c=new fabric.Group(e);this.shadow&&c.set("shadow",new fabric.Shadow(this.shadow)),this.canvas.fire("before:path:created",{path:c}),this.canvas.add(c),this.canvas.fire("path:created",{path:c}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=t,this.canvas.requestRenderAll()},_getOptimizedRects:function(t){var e,i,r,n={};for(i=0,r=t.length;r>i;i++)e=t[i].left+""+t[i].top,n[e]||(n[e]=t[i]);var o=[];for(e in n)o.push(n[e]);return o},render:function(t){var e,i,r=this.canvas.contextTop;for(r.fillStyle=this.color,this._saveAndTransform(r),e=0,i=t.length;i>e;e++){var n=t[e];"undefined"!=typeof n.opacity&&(r.globalAlpha=n.opacity),r.fillRect(n.x,n.y,n.width,n.width)}r.restore()},_render:function(){var t,e,i=this.canvas.contextTop;for(i.fillStyle=this.color,this._saveAndTransform(i),t=0,e=this.sprayChunks.length;e>t;t++)this.render(this.sprayChunks[t]);i.restore()},addSprayChunk:function(t){this.sprayChunkPoints=[];var e,i,r,n,o=this.width/2;for(n=0;n0&&!this.preserveObjectStacking){e=[],i=[];for(var n=0,o=this._objects.length;o>n;n++)t=this._objects[n],-1===r.indexOf(t)?e.push(t):i.push(t);r.length>1&&(this._activeObject._objects=i),e.push.apply(e,i)}else e=this._objects;return e},renderAll:function(){!this.contextTopDirty||this._groupSelector||this.isDrawingMode||(this.clearContext(this.contextTop),this.contextTopDirty=!1),this.hasLostContext&&(this.renderTopLayer(this.contextTop),this.hasLostContext=!1);var t=this.contextContainer;return this.renderCanvas(t,this._chooseObjectsToRender()),this},renderTopLayer:function(t){t.save(),this.isDrawingMode&&this._isCurrentlyDrawing&&(this.freeDrawingBrush&&this.freeDrawingBrush._render(),this.contextTopDirty=!0),this.selection&&this._groupSelector&&(this._drawSelection(t),this.contextTopDirty=!0),t.restore()},renderTop:function(){var t=this.contextTop;return this.clearContext(t),this.renderTopLayer(t),this.fire("after:render"),this},_normalizePointer:function(t,e){var i=t.calcTransformMatrix(),r=fabric.util.invertTransform(i),n=this.restorePointerVpt(e);return fabric.util.transformPoint(n,r)},isTargetTransparent:function(t,e,i){if(t.shouldCache()&&t._cacheCanvas&&t!==this._activeObject){var r=this._normalizePointer(t,{x:e,y:i}),n=Math.max(t.cacheTranslationX+r.x*t.zoomX,0),o=Math.max(t.cacheTranslationY+r.y*t.zoomY,0),s=fabric.util.isTransparent(t._cacheContext,Math.round(n),Math.round(o),this.targetFindTolerance);return s}var a=this.contextCache,c=t.selectionBackgroundColor,l=this.viewportTransform;t.selectionBackgroundColor="",this.clearContext(a),a.save(),a.transform(l[0],l[1],l[2],l[3],l[4],l[5]),t.render(a),a.restore(),t.selectionBackgroundColor=c;var s=fabric.util.isTransparent(a,e,i,this.targetFindTolerance);return s},_isSelectionKeyPressed:function(t){var e=!1;return e=Array.isArray(this.selectionKey)?!!this.selectionKey.find(function(e){return t[e]===!0}):t[this.selectionKey]},_shouldClearSelection:function(t,e){var i=this.getActiveObjects(),r=this._activeObject;return!e||e&&r&&i.length>1&&-1===i.indexOf(e)&&r!==e&&!this._isSelectionKeyPressed(t)||e&&!e.evented||e&&!e.selectable&&r&&r!==e},_shouldCenterTransform:function(t,e,i){if(t){var r;return"scale"===e||"scaleX"===e||"scaleY"===e||"resizing"===e?r=this.centeredScaling||t.centeredScaling:"rotate"===e&&(r=this.centeredRotation||t.centeredRotation),r?!i:i}},_getOriginFromCorner:function(t,e){var i={x:t.originX,y:t.originY};return"ml"===e||"tl"===e||"bl"===e?i.x="right":("mr"===e||"tr"===e||"br"===e)&&(i.x="left"),"tl"===e||"mt"===e||"tr"===e?i.y="bottom":("bl"===e||"mb"===e||"br"===e)&&(i.y="top"),i},_getActionFromCorner:function(t,e,i,r){if(!e||!t)return"drag";var n=r.controls[e];return n.getActionName(i,n,r)},_setupCurrentTransform:function(t,i,r){if(i){var n=this.getPointer(t),o=i.__corner,s=i.controls[o],a=r&&o?s.getActionHandler(t,i,s):fabric.controlsUtils.dragHandler,c=this._getActionFromCorner(r,o,t,i),l=this._getOriginFromCorner(i,o),h=t[this.centeredKey],u={target:i,action:c,actionHandler:a,corner:o,scaleX:i.scaleX,scaleY:i.scaleY,skewX:i.skewX,skewY:i.skewY,offsetX:n.x-i.left,offsetY:n.y-i.top,originX:l.x,originY:l.y,ex:n.x,ey:n.y,lastX:n.x,lastY:n.y,theta:e(i.angle),width:i.width*i.scaleX,shiftKey:t.shiftKey,altKey:h,original:fabric.util.saveObjectTransform(i)};this._shouldCenterTransform(i,c,h)&&(u.originX="center",u.originY="center"),u.original.originX=l.x,u.original.originY=l.y,this._currentTransform=u,this._beforeTransform(t)}},setCursor:function(t){this.upperCanvasEl.style.cursor=t},_drawSelection:function(t){var e=this._groupSelector,i=new fabric.Point(e.ex,e.ey),r=fabric.util.transformPoint(i,this.viewportTransform),n=new fabric.Point(e.ex+e.left,e.ey+e.top),o=fabric.util.transformPoint(n,this.viewportTransform),s=Math.min(r.x,o.x),a=Math.min(r.y,o.y),c=Math.max(r.x,o.x),l=Math.max(r.y,o.y),h=this.selectionLineWidth/2;this.selectionColor&&(t.fillStyle=this.selectionColor,t.fillRect(s,a,c-s,l-a)),this.selectionLineWidth&&this.selectionBorderColor&&(t.lineWidth=this.selectionLineWidth,t.strokeStyle=this.selectionBorderColor,s+=h,a+=h,c-=h,l-=h,fabric.Object.prototype._setLineDash.call(this,t,this.selectionDashArray),t.strokeRect(s,a,c-s,l-a))},findTarget:function(t,e){if(!this.skipTargetFind){var r,n,o=!0,s=this.getPointer(t,o),a=this._activeObject,c=this.getActiveObjects(),l=i(t),h=c.length>1&&!e||1===c.length;if(this.targets=[],h&&a._findTargetCorner(s,l))return a;if(c.length>1&&!e&&a===this._searchPossibleTargets([a],s))return a;if(1===c.length&&a===this._searchPossibleTargets([a],s)){if(!this.preserveObjectStacking)return a;r=a,n=this.targets,this.targets=[]}var u=this._searchPossibleTargets(this._objects,s);return t[this.altSelectionKey]&&u&&r&&u!==r&&(u=r,this.targets=n),u}},_checkTarget:function(t,e,i){if(e&&e.visible&&e.evented&&e.containsPoint(t)){if(!this.perPixelTargetFind&&!e.perPixelTargetFind||e.isEditing)return!0;var r=this.isTargetTransparent(e,i.x,i.y);if(!r)return!0}},_searchPossibleTargets:function(t,e){for(var i,r,n=t.length;n--;){var o=t[n],s=o.group?this._normalizePointer(o.group,e):e;if(this._checkTarget(s,o,e)){i=t[n],i.subTargetCheck&&i instanceof fabric.Group&&(r=this._searchPossibleTargets(i._objects,e),r&&this.targets.push(r));break}}return i},restorePointerVpt:function(t){return fabric.util.transformPoint(t,fabric.util.invertTransform(this.viewportTransform))},getPointer:function(e,i){if(this._absolutePointer&&!i)return this._absolutePointer;if(this._pointer&&i)return this._pointer;var r,n=t(e),o=this.upperCanvasEl,s=o.getBoundingClientRect(),a=s.width||0,c=s.height||0;a&&c||("top"in s&&"bottom"in s&&(c=Math.abs(s.top-s.bottom)),"right"in s&&"left"in s&&(a=Math.abs(s.right-s.left))),this.calcOffset(),n.x=n.x-this._offset.left,n.y=n.y-this._offset.top,i||(n=this.restorePointerVpt(n));var l=this.getRetinaScaling();return 1!==l&&(n.x/=l,n.y/=l),r=0===a||0===c?{width:1,height:1}:{width:o.width/a,height:o.height/c},{x:n.x*r.width,y:n.y*r.height}},_createUpperCanvas:function(){var t=this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/,""),e=this.lowerCanvasEl,i=this.upperCanvasEl;i?i.className="":(i=this._createCanvasElement(),this.upperCanvasEl=i),fabric.util.addClass(i,"upper-canvas "+t),this.wrapperEl.appendChild(i),this._copyCanvasStyle(e,i),this._applyCanvasStyle(i),this.contextTop=i.getContext("2d")},getTopContext:function(){return this.contextTop},_createCacheCanvas:function(){this.cacheCanvasEl=this._createCanvasElement(),this.cacheCanvasEl.setAttribute("width",this.width),this.cacheCanvasEl.setAttribute("height",this.height),this.contextCache=this.cacheCanvasEl.getContext("2d")},_initWrapperElement:function(){this.wrapperEl=fabric.util.wrapElement(this.lowerCanvasEl,"div",{"class":this.containerClass}),fabric.util.setStyle(this.wrapperEl,{width:this.width+"px",height:this.height+"px",position:"relative"}),fabric.util.makeElementUnselectable(this.wrapperEl)},_applyCanvasStyle:function(t){var e=this.width||t.width,i=this.height||t.height;fabric.util.setStyle(t,{position:"absolute",width:e+"px",height:i+"px",left:0,top:0,"touch-action":this.allowTouchScrolling?"manipulation":"none","-ms-touch-action":this.allowTouchScrolling?"manipulation":"none"}),t.width=e,t.height=i,fabric.util.makeElementUnselectable(t)},_copyCanvasStyle:function(t,e){e.style.cssText=t.style.cssText},getSelectionContext:function(){return this.contextTop},getSelectionElement:function(){return this.upperCanvasEl},getActiveObject:function(){return this._activeObject},getActiveObjects:function(){var t=this._activeObject;return t?"activeSelection"===t.type&&t._objects?t._objects.slice(0):[t]:[]},_onObjectRemoved:function(t){t===this._activeObject&&(this.fire("before:selection:cleared",{target:t}),this._discardActiveObject(),this.fire("selection:cleared",{target:t}),t.fire("deselected")),t===this._hoveredTarget&&(this._hoveredTarget=null,this._hoveredTargets=[]),this.callSuper("_onObjectRemoved",t)},_fireSelectionEvents:function(t,e){var i=!1,r=this.getActiveObjects(),n=[],o=[];t.forEach(function(t){-1===r.indexOf(t)&&(i=!0,t.fire("deselected",{e:e,target:t}),o.push(t))}),r.forEach(function(r){-1===t.indexOf(r)&&(i=!0,r.fire("selected",{e:e,target:r}),n.push(r))}),t.length>0&&r.length>0?i&&this.fire("selection:updated",{e:e,selected:n,deselected:o}):r.length>0?this.fire("selection:created",{e:e,selected:n}):t.length>0&&this.fire("selection:cleared",{e:e,deselected:o})},setActiveObject:function(t,e){var i=this.getActiveObjects();return this._setActiveObject(t,e),this._fireSelectionEvents(i,e),this},_setActiveObject:function(t,e){return this._activeObject===t?!1:this._discardActiveObject(e,t)?t.onSelect({e:e})?!1:(this._activeObject=t,!0):!1},_discardActiveObject:function(t,e){var i=this._activeObject;if(i){if(i.onDeselect({e:t,object:e}))return!1;this._activeObject=null}return!0},discardActiveObject:function(t){var e=this.getActiveObjects(),i=this.getActiveObject();return e.length&&this.fire("before:selection:cleared",{target:i,e:t}),this._discardActiveObject(t),this._fireSelectionEvents(e,t),this},dispose:function(){var t=this.wrapperEl;return this.removeListeners(),t.removeChild(this.upperCanvasEl),t.removeChild(this.lowerCanvasEl),this.contextCache=null,this.contextTop=null,["upperCanvasEl","cacheCanvasEl"].forEach(function(t){fabric.util.cleanUpJsdomNode(this[t]),this[t]=void 0}.bind(this)),t.parentNode&&t.parentNode.replaceChild(this.lowerCanvasEl,this.wrapperEl),delete this.wrapperEl,fabric.StaticCanvas.prototype.dispose.call(this),this},clear:function(){return this.discardActiveObject(),this.clearContext(this.contextTop),this.callSuper("clear")},drawControls:function(t){var e=this._activeObject;e&&e._renderControls(t)},_toObject:function(t,e,i){var r=this._realizeGroupTransformOnObject(t),n=this.callSuper("_toObject",t,e,i);return this._unwindGroupTransformOnObject(t,r),n},_realizeGroupTransformOnObject:function(t){if(t.group&&"activeSelection"===t.group.type&&this._activeObject===t.group){var e=["angle","flipX","flipY","left","scaleX","scaleY","skewX","skewY","top"],i={};return e.forEach(function(e){i[e]=t[e]}),fabric.util.addTransformToObject(t,this._activeObject.calcOwnMatrix()),i}return null},_unwindGroupTransformOnObject:function(t,e){e&&t.set(e)},_setSVGObject:function(t,e,i){var r=this._realizeGroupTransformOnObject(e);this.callSuper("_setSVGObject",t,e,i),this._unwindGroupTransformOnObject(e,r)},setViewportTransform:function(t){this.renderOnAddRemove&&this._activeObject&&this._activeObject.isEditing&&this._activeObject.clearContextTop(),fabric.StaticCanvas.prototype.setViewportTransform.call(this,t)}});for(var r in fabric.StaticCanvas)"prototype"!==r&&(fabric.Canvas[r]=fabric.StaticCanvas[r])}();!function(){function t(t,e){return t.button&&t.button===e-1}var e=fabric.util.addListener,i=fabric.util.removeListener,r=3,n=2,o=1,s={passive:!1};fabric.util.object.extend(fabric.Canvas.prototype,{mainTouchId:null,_initEventListeners:function(){this.removeListeners(),this._bindEvents(),this.addOrRemove(e,"add")},_getEventPrefix:function(){return this.enablePointerEvents?"pointer":"mouse"},addOrRemove:function(t,e){var i=this.upperCanvasEl,r=this._getEventPrefix();t(fabric.window,"resize",this._onResize),t(i,r+"down",this._onMouseDown),t(i,r+"move",this._onMouseMove,s),t(i,r+"out",this._onMouseOut),t(i,r+"enter",this._onMouseEnter),t(i,"wheel",this._onMouseWheel),t(i,"contextmenu",this._onContextMenu),t(i,"dblclick",this._onDoubleClick),t(i,"dragover",this._onDragOver),t(i,"dragenter",this._onDragEnter),t(i,"dragleave",this._onDragLeave),t(i,"drop",this._onDrop),this.enablePointerEvents||t(i,"touchstart",this._onTouchStart,s),"undefined"!=typeof eventjs&&e in eventjs&&(eventjs[e](i,"gesture",this._onGesture),eventjs[e](i,"drag",this._onDrag),eventjs[e](i,"orientation",this._onOrientationChange),eventjs[e](i,"shake",this._onShake),eventjs[e](i,"longpress",this._onLongPress))},removeListeners:function(){this.addOrRemove(i,"remove");var t=this._getEventPrefix();i(fabric.document,t+"up",this._onMouseUp),i(fabric.document,"touchend",this._onTouchEnd,s),i(fabric.document,t+"move",this._onMouseMove,s),i(fabric.document,"touchmove",this._onMouseMove,s)},_bindEvents:function(){this.eventsBound||(this._onMouseDown=this._onMouseDown.bind(this),this._onTouchStart=this._onTouchStart.bind(this),this._onMouseMove=this._onMouseMove.bind(this),this._onMouseUp=this._onMouseUp.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onResize=this._onResize.bind(this),this._onGesture=this._onGesture.bind(this),this._onDrag=this._onDrag.bind(this),this._onShake=this._onShake.bind(this),this._onLongPress=this._onLongPress.bind(this),this._onOrientationChange=this._onOrientationChange.bind(this),this._onMouseWheel=this._onMouseWheel.bind(this),this._onMouseOut=this._onMouseOut.bind(this),this._onMouseEnter=this._onMouseEnter.bind(this),this._onContextMenu=this._onContextMenu.bind(this),this._onDoubleClick=this._onDoubleClick.bind(this),this._onDragOver=this._onDragOver.bind(this),this._onDragEnter=this._simpleEventHandler.bind(this,"dragenter"),this._onDragLeave=this._simpleEventHandler.bind(this,"dragleave"),this._onDrop=this._onDrop.bind(this),this.eventsBound=!0)},_onGesture:function(t,e){this.__onTransformGesture&&this.__onTransformGesture(t,e)},_onDrag:function(t,e){this.__onDrag&&this.__onDrag(t,e)},_onMouseWheel:function(t){this.__onMouseWheel(t)},_onMouseOut:function(t){var e=this._hoveredTarget;this.fire("mouse:out",{target:e,e:t}),this._hoveredTarget=null,e&&e.fire("mouseout",{e:t});var i=this;this._hoveredTargets.forEach(function(r){i.fire("mouse:out",{target:e,e:t}),r&&e.fire("mouseout",{e:t})}),this._hoveredTargets=[],this._iTextInstances&&this._iTextInstances.forEach(function(t){t.isEditing&&t.hiddenTextarea.focus()})},_onMouseEnter:function(t){this._currentTransform||this.findTarget(t)||(this.fire("mouse:over",{target:null,e:t}),this._hoveredTarget=null,this._hoveredTargets=[])},_onOrientationChange:function(t,e){this.__onOrientationChange&&this.__onOrientationChange(t,e)},_onShake:function(t,e){this.__onShake&&this.__onShake(t,e)},_onLongPress:function(t,e){this.__onLongPress&&this.__onLongPress(t,e)},_onDragOver:function(t){t.preventDefault();var e=this._simpleEventHandler("dragover",t);this._fireEnterLeaveEvents(e,t)},_onDrop:function(t){return this._simpleEventHandler("drop:before",t),this._simpleEventHandler("drop",t)},_onContextMenu:function(t){return this.stopContextMenu&&(t.stopPropagation(),t.preventDefault()),!1},_onDoubleClick:function(t){this._cacheTransformEventData(t),this._handleEvent(t,"dblclick"),this._resetTransformEventData(t)},getPointerId:function(t){var e=t.changedTouches;return e?e[0]&&e[0].identifier:this.enablePointerEvents?t.pointerId:-1},_isMainEvent:function(t){return t.isPrimary===!0?!0:t.isPrimary===!1?!1:"touchend"===t.type&&0===t.touches.length?!0:t.changedTouches?t.changedTouches[0].identifier===this.mainTouchId:!0},_onTouchStart:function(t){t.preventDefault(),null===this.mainTouchId&&(this.mainTouchId=this.getPointerId(t)),this.__onMouseDown(t),this._resetTransformEventData();var r=this.upperCanvasEl,n=this._getEventPrefix();e(fabric.document,"touchend",this._onTouchEnd,s),e(fabric.document,"touchmove",this._onMouseMove,s),i(r,n+"down",this._onMouseDown)},_onMouseDown:function(t){this.__onMouseDown(t),this._resetTransformEventData();var r=this.upperCanvasEl,n=this._getEventPrefix();i(r,n+"move",this._onMouseMove,s),e(fabric.document,n+"up",this._onMouseUp),e(fabric.document,n+"move",this._onMouseMove,s)},_onTouchEnd:function(t){if(!(t.touches.length>0)){this.__onMouseUp(t),this._resetTransformEventData(),this.mainTouchId=null;var r=this._getEventPrefix();i(fabric.document,"touchend",this._onTouchEnd,s),i(fabric.document,"touchmove",this._onMouseMove,s);var n=this;this._willAddMouseDown&&clearTimeout(this._willAddMouseDown),this._willAddMouseDown=setTimeout(function(){e(n.upperCanvasEl,r+"down",n._onMouseDown),n._willAddMouseDown=0},400)}},_onMouseUp:function(t){this.__onMouseUp(t),this._resetTransformEventData();var r=this.upperCanvasEl,n=this._getEventPrefix();this._isMainEvent(t)&&(i(fabric.document,n+"up",this._onMouseUp),i(fabric.document,n+"move",this._onMouseMove,s),e(r,n+"move",this._onMouseMove,s))},_onMouseMove:function(t){!this.allowTouchScrolling&&t.preventDefault&&t.preventDefault(),this.__onMouseMove(t)},_onResize:function(){this.calcOffset()},_shouldRender:function(t){var e=this._activeObject;return!!e!=!!t||e&&t&&e!==t?!0:e&&e.isEditing?!1:!1},__onMouseUp:function(e){var i,s=this._currentTransform,a=this._groupSelector,c=!1,h=!a||0===a.left&&0===a.top;if(this._cacheTransformEventData(e),i=this._target,this._handleEvent(e,"up:before"),t(e,r))return void(this.fireRightClick&&this._handleEvent(e,"up",r,h));if(t(e,n))return this.fireMiddleClick&&this._handleEvent(e,"up",n,h),void this._resetTransformEventData();if(this.isDrawingMode&&this._isCurrentlyDrawing)return void this._onMouseUpInDrawingMode(e);if(this._isMainEvent(e)){if(s&&(this._finalizeCurrentTransform(e),c=s.actionPerformed),!h){var l=i===this._activeObject;this._maybeGroupObjects(e),c||(c=this._shouldRender(i)||!l&&i===this._activeObject)}var u,f;if(i){if(u=i._findTargetCorner(this.getPointer(e,!0),fabric.util.isTouchEvent(e)),i.selectable&&i!==this._activeObject&&"up"===i.activeOn)this.setActiveObject(i,e),c=!0;else{var d=i.controls[u],g=d&&d.getMouseUpHandler(e,i,d);g&&(f=this.getPointer(e),g(e,s,f.x,f.y))}i.isMoving=!1}if(s&&(s.target!==i||s.corner!==u)){var p=s.target&&s.target.controls[s.corner],v=p&&p.getMouseUpHandler(e,i,d);f=f||this.getPointer(e),v&&v(e,s,f.x,f.y)}this._setCursorFromEvent(e,i),this._handleEvent(e,"up",o,h),this._groupSelector=null,this._currentTransform=null,i&&(i.__corner=0),c?this.requestRenderAll():h||this.renderTop()}},_simpleEventHandler:function(t,e){var i=this.findTarget(e),r=this.targets,n={e:e,target:i,subTargets:r};if(this.fire(t,n),i&&i.fire(t,n),!r)return i;for(var o=0;os;s++)this.fireSyntheticInOutEvents(n[s],e,{oldTarget:r[s],evtOut:"mouseout",evtIn:"mouseover"});this._hoveredTarget=t,this._hoveredTargets=this.targets.concat()},_fireEnterLeaveEvents:function(t,e){var i=this._draggedoverTarget,r=this._hoveredTargets,n=this.targets,o=Math.max(r.length,n.length);this.fireSyntheticInOutEvents(t,e,{oldTarget:i,evtOut:"dragleave",evtIn:"dragenter"});for(var s=0;o>s;s++)this.fireSyntheticInOutEvents(n[s],e,{oldTarget:r[s],evtOut:"dragleave",evtIn:"dragenter"});this._draggedoverTarget=t},fireSyntheticInOutEvents:function(t,e,i){var r,n,o,s,a=i.oldTarget,c=a!==t,h=i.canvasEvtIn,l=i.canvasEvtOut;c&&(r={e:e,target:t,previousTarget:a},n={e:e,target:a,nextTarget:t}),s=t&&c,o=a&&c,o&&(l&&this.fire(l,n),a.fire(i.evtOut,n)),s&&(h&&this.fire(h,r),t.fire(i.evtIn,r))},__onMouseWheel:function(t){this._cacheTransformEventData(t),this._handleEvent(t,"wheel"),this._resetTransformEventData()},_transformObject:function(t){var e=this.getPointer(t),i=this._currentTransform;i.reset=!1,i.shiftKey=t.shiftKey,i.altKey=t[this.centeredKey],this._performTransformAction(t,i,e),i.actionPerformed&&this.requestRenderAll()},_performTransformAction:function(t,e,i){var r=i.x,n=i.y,o=e.action,s=!1,a=e.actionHandler;a&&(s=a(t,e,r,n)),"drag"===o&&s&&(e.target.isMoving=!0,this.setCursor(e.target.moveCursor||this.moveCursor)),e.actionPerformed=e.actionPerformed||s},_fire:fabric.controlsUtils.fireEvent,_setCursorFromEvent:function(t,e){if(!e)return this.setCursor(this.defaultCursor),!1;var i=e.hoverCursor||this.hoverCursor,r=this._activeObject&&"activeSelection"===this._activeObject.type?this._activeObject:null,n=(!r||!r.contains(e))&&e._findTargetCorner(this.getPointer(t,!0));n?this.setCursor(this.getCornerCursor(n,e,t)):(e.subTargetCheck&&this.targets.concat().reverse().map(function(t){i=t.hoverCursor||i}),this.setCursor(i))},getCornerCursor:function(t,e,i){var r=e.controls[t];return r.cursorStyleHandler(i,r,e)}})}();!function(){var t=Math.min,e=Math.max;fabric.util.object.extend(fabric.Canvas.prototype,{_shouldGroup:function(t,e){var i=this._activeObject;return i&&this._isSelectionKeyPressed(t)&&e&&e.selectable&&this.selection&&(i!==e||"activeSelection"===i.type)&&!e.onSelect({e:t})},_handleGrouping:function(t,e){var i=this._activeObject;i.__corner||(e!==i||(e=this.findTarget(t,!0),e&&e.selectable))&&(i&&"activeSelection"===i.type?this._updateActiveSelection(e,t):this._createActiveSelection(e,t))},_updateActiveSelection:function(t,e){var i=this._activeObject,r=i._objects.slice(0);i.contains(t)?(i.removeWithUpdate(t),this._hoveredTarget=t,this._hoveredTargets=this.targets.concat(),1===i.size()&&this._setActiveObject(i.item(0),e)):(i.addWithUpdate(t),this._hoveredTarget=i,this._hoveredTargets=this.targets.concat()),this._fireSelectionEvents(r,e)},_createActiveSelection:function(t,e){var i=this.getActiveObjects(),r=this._createGroup(t);this._hoveredTarget=r,this._setActiveObject(r,e),this._fireSelectionEvents(i,e)},_createGroup:function(t){var e=this._objects,i=e.indexOf(this._activeObject)1&&(e=new fabric.ActiveSelection(i.reverse(),{canvas:this}),this.setActiveObject(e,t))},_collectObjects:function(i){for(var r,n=[],o=this._groupSelector.ex,s=this._groupSelector.ey,a=o+this._groupSelector.left,c=s+this._groupSelector.top,h=new fabric.Point(t(o,a),t(s,c)),l=new fabric.Point(e(o,a),e(s,c)),u=!this.selectionFullyContained,f=o===a&&s===c,d=this._objects.length;d--&&(r=this._objects[d],!(r&&r.selectable&&r.visible&&(u&&r.intersectsWithRect(h,l,!0)||r.isContainedWithinRect(h,l,!0)||u&&r.containsPoint(h,null,!0)||u&&r.containsPoint(l,null,!0))&&(n.push(r),f))););return n.length>1&&(n=n.filter(function(t){return!t.onSelect({e:i})})),n},_maybeGroupObjects:function(t){this.selection&&this._groupSelector&&this._groupSelectedObjects(t),this.setCursor(this.defaultCursor),this._groupSelector=null}})}();!function(){fabric.util.object.extend(fabric.StaticCanvas.prototype,{toDataURL:function(t){t||(t={});var e=t.format||"png",i=t.quality||1,r=(t.multiplier||1)*(t.enableRetinaScaling?this.getRetinaScaling():1),n=this.toCanvasElement(r,t);return fabric.util.toDataURL(n,e,i)},toCanvasElement:function(t,e){t=t||1,e=e||{};var i=(e.width||this.width)*t,r=(e.height||this.height)*t,n=this.getZoom(),o=this.width,s=this.height,a=n*t,c=this.viewportTransform,h=(c[4]-(e.left||0))*t,l=(c[5]-(e.top||0))*t,u=this.interactive,f=[a,0,0,a,h,l],d=this.enableRetinaScaling,g=fabric.util.createCanvasElement(),p=this.contextTop;return g.width=i,g.height=r,this.contextTop=null,this.enableRetinaScaling=!1,this.interactive=!1,this.viewportTransform=f,this.width=i,this.height=r,this.calcViewportBoundaries(),this.renderCanvas(g.getContext("2d"),this._objects),this.viewportTransform=c,this.width=o,this.height=s,this.calcViewportBoundaries(),this.interactive=u,this.enableRetinaScaling=d,this.contextTop=p,g}})}();fabric.util.object.extend(fabric.StaticCanvas.prototype,{loadFromJSON:function(t,e,i){if(t){var r="string"==typeof t?JSON.parse(t):fabric.util.object.clone(t),n=this,o=r.clipPath,s=this.renderOnAddRemove;return this.renderOnAddRemove=!1,delete r.clipPath,this._enlivenObjects(r.objects,function(t){n.clear(),n._setBgOverlay(r,function(){o?n._enlivenObjects([o],function(i){n.clipPath=i[0],n.__setupCanvas.call(n,r,t,s,e)}):n.__setupCanvas.call(n,r,t,s,e)})},i),this}},__setupCanvas:function(t,e,i,r){var n=this;e.forEach(function(t,e){n.insertAt(t,e)}),this.renderOnAddRemove=i,delete t.objects,delete t.backgroundImage,delete t.overlayImage,delete t.background,delete t.overlay,this._setOptions(t),this.renderAll(),r&&r()},_setBgOverlay:function(t,e){var i={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!(t.backgroundImage||t.overlayImage||t.background||t.overlay))return void(e&&e());var r=function(){i.backgroundImage&&i.overlayImage&&i.backgroundColor&&i.overlayColor&&e&&e()};this.__setBgOverlay("backgroundImage",t.backgroundImage,i,r),this.__setBgOverlay("overlayImage",t.overlayImage,i,r),this.__setBgOverlay("backgroundColor",t.background,i,r),this.__setBgOverlay("overlayColor",t.overlay,i,r)},__setBgOverlay:function(t,e,i,r){var n=this;return e?void("backgroundImage"===t||"overlayImage"===t?fabric.util.enlivenObjects([e],function(e){n[t]=e[0],i[t]=!0,r&&r()}):this["set"+fabric.util.string.capitalize(t,!0)](e,function(){i[t]=!0,r&&r()})):(i[t]=!0,void(r&&r()))},_enlivenObjects:function(t,e,i){return t&&0!==t.length?void fabric.util.enlivenObjects(t,function(t){e&&e(t)},null,i):void(e&&e([]))},_toDataURL:function(t,e){this.clone(function(i){e(i.toDataURL(t))})},_toDataURLWithMultiplier:function(t,e,i){this.clone(function(r){i(r.toDataURLWithMultiplier(t,e))})},clone:function(t,e){var i=JSON.stringify(this.toJSON(e));this.cloneWithoutData(function(e){e.loadFromJSON(i,function(){t&&t(e)})})},cloneWithoutData:function(t){var e=fabric.util.createCanvasElement();e.width=this.width,e.height=this.height;var i=new fabric.Canvas(e);this.backgroundImage?(i.setBackgroundImage(this.backgroundImage.src,function(){i.renderAll(),t&&t(i)}),i.backgroundImageOpacity=this.backgroundImageOpacity,i.backgroundImageStretch=this.backgroundImageStretch):t&&t(i)}});!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend,r=e.util.object.clone,n=e.util.toFixed,s=e.util.string.capitalize,o=e.util.degreesToRadians,a=!e.isLikelyNode,c=2;e.Object||(e.Object=e.util.createClass(e.CommonMethods,{type:"object",originX:"left",originY:"top",top:0,left:0,width:0,height:0,scaleX:1,scaleY:1,flipX:!1,flipY:!1,opacity:1,angle:0,skewX:0,skewY:0,cornerSize:13,touchCornerSize:24,transparentCorners:!0,hoverCursor:null,moveCursor:null,padding:0,borderColor:"rgb(178,204,255)",borderDashArray:null,cornerColor:"rgb(178,204,255)",cornerStrokeColor:null,cornerStyle:"rect",cornerDashArray:null,centeredScaling:!1,centeredRotation:!0,fill:"rgb(0,0,0)",fillRule:"nonzero",globalCompositeOperation:"source-over",backgroundColor:"",selectionBackgroundColor:"",stroke:null,strokeWidth:1,strokeDashArray:null,strokeDashOffset:0,strokeLineCap:"butt",strokeLineJoin:"miter",strokeMiterLimit:4,shadow:null,borderOpacityWhenMoving:.4,borderScaleFactor:1,minScaleLimit:0,selectable:!0,evented:!0,visible:!0,hasControls:!0,hasBorders:!0,perPixelTargetFind:!1,includeDefaultValues:!0,lockMovementX:!1,lockMovementY:!1,lockRotation:!1,lockScalingX:!1,lockScalingY:!1,lockSkewingX:!1,lockSkewingY:!1,lockScalingFlip:!1,excludeFromExport:!1,objectCaching:a,statefullCache:!1,noScaleCache:!0,strokeUniform:!1,dirty:!0,__corner:0,paintFirst:"fill",activeOn:"down",stateProperties:"top left width height scaleX scaleY flipX flipY originX originY transformMatrix stroke strokeWidth strokeDashArray strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit angle opacity fill globalCompositeOperation shadow visible backgroundColor skewX skewY fillRule paintFirst clipPath strokeUniform".split(" "),cacheProperties:"fill stroke strokeWidth strokeDashArray width height paintFirst strokeUniform strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath".split(" "),colorProperties:"fill stroke backgroundColor".split(" "),clipPath:void 0,inverted:!1,absolutePositioned:!1,initialize:function(t){t&&this.setOptions(t)},_createCacheCanvas:function(){this._cacheProperties={},this._cacheCanvas=e.util.createCanvasElement(),this._cacheContext=this._cacheCanvas.getContext("2d"),this._updateCacheCanvas(),this.dirty=!0},_limitCacheSize:function(t){var i=e.perfLimitSizeTotal,r=t.width,n=t.height,s=e.maxCacheSideLimit,o=e.minCacheSideLimit;if(s>=r&&s>=n&&i>=r*n)return o>r&&(t.width=o),o>n&&(t.height=o),t;var a=r/n,c=e.util.limitDimsByArea(a,i),h=e.util.capValue,l=h(o,c.x,s),u=h(o,c.y,s);return r>l&&(t.zoomX/=r/l,t.width=l,t.capped=!0),n>u&&(t.zoomY/=n/u,t.height=u,t.capped=!0),t},_getCacheCanvasDimensions:function(){var t=this.getTotalObjectScaling(),e=this._getTransformedDimensions(0,0),i=e.x*t.scaleX/this.scaleX,r=e.y*t.scaleY/this.scaleY;return{width:i+c,height:r+c,zoomX:t.scaleX,zoomY:t.scaleY,x:i,y:r}},_updateCacheCanvas:function(){var t=this.canvas;if(this.noScaleCache&&t&&t._currentTransform){var i=t._currentTransform.target,r=t._currentTransform.action;if(this===i&&r.slice&&"scale"===r.slice(0,5))return!1}var n,s,o=this._cacheCanvas,a=this._limitCacheSize(this._getCacheCanvasDimensions()),c=e.minCacheSideLimit,h=a.width,l=a.height,u=a.zoomX,f=a.zoomY,d=h!==this.cacheWidth||l!==this.cacheHeight,g=this.zoomX!==u||this.zoomY!==f,p=d||g,v=0,m=0,b=!1;if(d){var y=this._cacheCanvas.width,_=this._cacheCanvas.height,x=h>y||l>_,C=(.9*y>h||.9*_>l)&&y>c&&_>c;b=x||C,x&&!a.capped&&(h>c||l>c)&&(v=.1*h,m=.1*l)}return this instanceof e.Text&&this.path&&(p=!0,b=!0,v+=this.getHeightOfLine(0)*this.zoomX,m+=this.getHeightOfLine(0)*this.zoomY),p?(b?(o.width=Math.ceil(h+v),o.height=Math.ceil(l+m)):(this._cacheContext.setTransform(1,0,0,1,0,0),this._cacheContext.clearRect(0,0,o.width,o.height)),n=a.x/2,s=a.y/2,this.cacheTranslationX=Math.round(o.width/2-n)+n,this.cacheTranslationY=Math.round(o.height/2-s)+s,this.cacheWidth=h,this.cacheHeight=l,this._cacheContext.translate(this.cacheTranslationX,this.cacheTranslationY),this._cacheContext.scale(u,f),this.zoomX=u,this.zoomY=f,!0):!1},setOptions:function(t){this._setOptions(t),this._initGradient(t.fill,"fill"),this._initGradient(t.stroke,"stroke"),this._initPattern(t.fill,"fill"),this._initPattern(t.stroke,"stroke")},transform:function(t){var e=this.group&&!this.group._transformDone||this.group&&this.canvas&&t===this.canvas.contextTop,i=this.calcTransformMatrix(!e);t.transform(i[0],i[1],i[2],i[3],i[4],i[5])},toObject:function(t){var i=e.Object.NUM_FRACTION_DIGITS,r={type:this.type,version:e.version,originX:this.originX,originY:this.originY,left:n(this.left,i),top:n(this.top,i),width:n(this.width,i),height:n(this.height,i),fill:this.fill&&this.fill.toObject?this.fill.toObject():this.fill,stroke:this.stroke&&this.stroke.toObject?this.stroke.toObject():this.stroke,strokeWidth:n(this.strokeWidth,i),strokeDashArray:this.strokeDashArray?this.strokeDashArray.concat():this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeDashOffset:this.strokeDashOffset,strokeLineJoin:this.strokeLineJoin,strokeUniform:this.strokeUniform,strokeMiterLimit:n(this.strokeMiterLimit,i),scaleX:n(this.scaleX,i),scaleY:n(this.scaleY,i),angle:n(this.angle,i),flipX:this.flipX,flipY:this.flipY,opacity:n(this.opacity,i),shadow:this.shadow&&this.shadow.toObject?this.shadow.toObject():this.shadow,visible:this.visible,backgroundColor:this.backgroundColor,fillRule:this.fillRule,paintFirst:this.paintFirst,globalCompositeOperation:this.globalCompositeOperation,skewX:n(this.skewX,i),skewY:n(this.skewY,i)};return this.clipPath&&!this.clipPath.excludeFromExport&&(r.clipPath=this.clipPath.toObject(t),r.clipPath.inverted=this.clipPath.inverted,r.clipPath.absolutePositioned=this.clipPath.absolutePositioned),e.util.populateWithProperties(this,r,t),this.includeDefaultValues||(r=this._removeDefaultValues(r)),r},toDatalessObject:function(t){return this.toObject(t)},_removeDefaultValues:function(t){var i=e.util.getKlass(t.type).prototype,r=i.stateProperties;return r.forEach(function(e){"left"!==e&&"top"!==e&&(t[e]===i[e]&&delete t[e],Array.isArray(t[e])&&Array.isArray(i[e])&&0===t[e].length&&0===i[e].length&&delete t[e])}),t},toString:function(){return"#"},getObjectScaling:function(){if(!this.group)return{scaleX:this.scaleX,scaleY:this.scaleY};var t=e.util.qrDecompose(this.calcTransformMatrix());return{scaleX:Math.abs(t.scaleX),scaleY:Math.abs(t.scaleY)}},getTotalObjectScaling:function(){var t=this.getObjectScaling(),e=t.scaleX,i=t.scaleY;if(this.canvas){var r=this.canvas.getZoom(),n=this.canvas.getRetinaScaling();e*=r*n,i*=r*n}return{scaleX:e,scaleY:i}},getObjectOpacity:function(){var t=this.opacity;return this.group&&(t*=this.group.getObjectOpacity()),t},_set:function(t,i){var r="scaleX"===t||"scaleY"===t,n=this[t]!==i,s=!1;return r&&(i=this._constrainScale(i)),"scaleX"===t&&0>i?(this.flipX=!this.flipX,i*=-1):"scaleY"===t&&0>i?(this.flipY=!this.flipY,i*=-1):"shadow"!==t||!i||i instanceof e.Shadow?"dirty"===t&&this.group&&this.group.set("dirty",i):i=new e.Shadow(i),this[t]=i,n&&(s=this.group&&this.group.isOnACache(),this.cacheProperties.indexOf(t)>-1?(this.dirty=!0,s&&this.group.set("dirty",!0)):s&&this.stateProperties.indexOf(t)>-1&&this.group.set("dirty",!0)),this},setOnGroup:function(){},getViewportTransform:function(){return this.canvas&&this.canvas.viewportTransform?this.canvas.viewportTransform:e.iMatrix.concat()},isNotVisible:function(){return 0===this.opacity||!this.width&&!this.height&&0===this.strokeWidth||!this.visible},render:function(t){this.isNotVisible()||(!this.canvas||!this.canvas.skipOffscreen||this.group||this.isOnScreen())&&(t.save(),this._setupCompositeOperation(t),this.drawSelectionBackground(t),this.transform(t),this._setOpacity(t),this._setShadow(t,this),this.shouldCache()?(this.renderCache(),this.drawCacheOnCanvas(t)):(this._removeCacheCanvas(),this.dirty=!1,this.drawObject(t),this.objectCaching&&this.statefullCache&&this.saveState({propertySet:"cacheProperties"})),t.restore())},renderCache:function(t){t=t||{},this._cacheCanvas&&this._cacheContext||this._createCacheCanvas(),this.isCacheDirty()&&(this.statefullCache&&this.saveState({propertySet:"cacheProperties"}),this.drawObject(this._cacheContext,t.forClipping),this.dirty=!1)},_removeCacheCanvas:function(){this._cacheCanvas=null,this._cacheContext=null,this.cacheWidth=0,this.cacheHeight=0},hasStroke:function(){return this.stroke&&"transparent"!==this.stroke&&0!==this.strokeWidth},hasFill:function(){return this.fill&&"transparent"!==this.fill},needsItsOwnCache:function(){return"stroke"===this.paintFirst&&this.hasFill()&&this.hasStroke()&&"object"==typeof this.shadow?!0:this.clipPath?!0:!1},shouldCache:function(){return this.ownCaching=this.needsItsOwnCache()||this.objectCaching&&(!this.group||!this.group.isOnACache()),this.ownCaching},willDrawShadow:function(){return!!this.shadow&&(0!==this.shadow.offsetX||0!==this.shadow.offsetY)},drawClipPathOnCache:function(t,i){if(t.save(),t.globalCompositeOperation=i.inverted?"destination-out":"destination-in",i.absolutePositioned){var r=e.util.invertTransform(this.calcTransformMatrix());t.transform(r[0],r[1],r[2],r[3],r[4],r[5])}i.transform(t),t.scale(1/i.zoomX,1/i.zoomY),t.drawImage(i._cacheCanvas,-i.cacheTranslationX,-i.cacheTranslationY),t.restore()},drawObject:function(t,e){var i=this.fill,r=this.stroke;e?(this.fill="black",this.stroke="",this._setClippingProperties(t)):this._renderBackground(t),this._render(t),this._drawClipPath(t,this.clipPath),this.fill=i,this.stroke=r},_drawClipPath:function(t,e){e&&(e.canvas=this.canvas,e.shouldCache(),e._transformDone=!0,e.renderCache({forClipping:!0}),this.drawClipPathOnCache(t,e))},drawCacheOnCanvas:function(t){t.scale(1/this.zoomX,1/this.zoomY),t.drawImage(this._cacheCanvas,-this.cacheTranslationX,-this.cacheTranslationY)},isCacheDirty:function(t){if(this.isNotVisible())return!1;if(this._cacheCanvas&&this._cacheContext&&!t&&this._updateCacheCanvas())return!0;if(this.dirty||this.clipPath&&this.clipPath.absolutePositioned||this.statefullCache&&this.hasStateChanged("cacheProperties")){if(this._cacheCanvas&&this._cacheContext&&!t){var e=this.cacheWidth/this.zoomX,i=this.cacheHeight/this.zoomY;this._cacheContext.clearRect(-e/2,-i/2,e,i)}return!0}return!1},_renderBackground:function(t){if(this.backgroundColor){var e=this._getNonTransformedDimensions();t.fillStyle=this.backgroundColor,t.fillRect(-e.x/2,-e.y/2,e.x,e.y),this._removeShadow(t)}},_setOpacity:function(t){this.group&&!this.group._transformDone?t.globalAlpha=this.getObjectOpacity():t.globalAlpha*=this.opacity},_setStrokeStyles:function(t,e){var i=e.stroke;i&&(t.lineWidth=e.strokeWidth,t.lineCap=e.strokeLineCap,t.lineDashOffset=e.strokeDashOffset,t.lineJoin=e.strokeLineJoin,t.miterLimit=e.strokeMiterLimit,i.toLive?"percentage"===i.gradientUnits||i.gradientTransform||i.patternTransform?this._applyPatternForTransformedGradient(t,i):(t.strokeStyle=i.toLive(t,this),this._applyPatternGradientTransform(t,i)):t.strokeStyle=e.stroke)},_setFillStyles:function(t,e){var i=e.fill;i&&(i.toLive?(t.fillStyle=i.toLive(t,this),this._applyPatternGradientTransform(t,e.fill)):t.fillStyle=i)},_setClippingProperties:function(t){t.globalAlpha=1,t.strokeStyle="transparent",t.fillStyle="#000000"},_setLineDash:function(t,e){e&&0!==e.length&&(1&e.length&&e.push.apply(e,e),t.setLineDash(e))},_renderControls:function(t,i){var r,n,s,a=this.getViewportTransform(),c=this.calcTransformMatrix();i=i||{},n="undefined"!=typeof i.hasBorders?i.hasBorders:this.hasBorders,s="undefined"!=typeof i.hasControls?i.hasControls:this.hasControls,c=e.util.multiplyTransformMatrices(a,c),r=e.util.qrDecompose(c),t.save(),t.translate(r.translateX,r.translateY),t.lineWidth=1*this.borderScaleFactor,this.group||(t.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1),this.flipX&&(r.angle-=180),t.rotate(o(this.group?r.angle:this.angle)),i.forActiveSelection||this.group?n&&this.drawBordersInGroup(t,r,i):n&&this.drawBorders(t,i),s&&this.drawControls(t,i),t.restore()},_setShadow:function(t){if(this.shadow){var i,r=this.shadow,n=this.canvas,s=n&&n.viewportTransform[0]||1,o=n&&n.viewportTransform[3]||1;i=r.nonScaling?{scaleX:1,scaleY:1}:this.getObjectScaling(),n&&n._isRetinaScaling()&&(s*=e.devicePixelRatio,o*=e.devicePixelRatio),t.shadowColor=r.color,t.shadowBlur=r.blur*e.browserShadowBlurConstant*(s+o)*(i.scaleX+i.scaleY)/4,t.shadowOffsetX=r.offsetX*s*i.scaleX,t.shadowOffsetY=r.offsetY*o*i.scaleY}},_removeShadow:function(t){this.shadow&&(t.shadowColor="",t.shadowBlur=t.shadowOffsetX=t.shadowOffsetY=0)},_applyPatternGradientTransform:function(t,e){if(!e||!e.toLive)return{offsetX:0,offsetY:0};var i=e.gradientTransform||e.patternTransform,r=-this.width/2+e.offsetX||0,n=-this.height/2+e.offsetY||0;return"percentage"===e.gradientUnits?t.transform(this.width,0,0,this.height,r,n):t.transform(1,0,0,1,r,n),i&&t.transform(i[0],i[1],i[2],i[3],i[4],i[5]),{offsetX:r,offsetY:n}},_renderPaintInOrder:function(t){"stroke"===this.paintFirst?(this._renderStroke(t),this._renderFill(t)):(this._renderFill(t),this._renderStroke(t))},_render:function(){},_renderFill:function(t){this.fill&&(t.save(),this._setFillStyles(t,this),"evenodd"===this.fillRule?t.fill("evenodd"):t.fill(),t.restore())},_renderStroke:function(t){if(this.stroke&&0!==this.strokeWidth){if(this.shadow&&!this.shadow.affectStroke&&this._removeShadow(t),t.save(),this.strokeUniform&&this.group){var e=this.getObjectScaling();t.scale(1/e.scaleX,1/e.scaleY)}else this.strokeUniform&&t.scale(1/this.scaleX,1/this.scaleY);this._setLineDash(t,this.strokeDashArray),this._setStrokeStyles(t,this),t.stroke(),t.restore()}},_applyPatternForTransformedGradient:function(t,i){var r,n=this._limitCacheSize(this._getCacheCanvasDimensions()),s=e.util.createCanvasElement(),o=this.canvas.getRetinaScaling(),a=n.x/this.scaleX/o,c=n.y/this.scaleY/o;s.width=a,s.height=c,r=s.getContext("2d"),r.beginPath(),r.moveTo(0,0),r.lineTo(a,0),r.lineTo(a,c),r.lineTo(0,c),r.closePath(),r.translate(a/2,c/2),r.scale(n.zoomX/this.scaleX/o,n.zoomY/this.scaleY/o),this._applyPatternGradientTransform(r,i),r.fillStyle=i.toLive(t),r.fill(),t.translate(-this.width/2-this.strokeWidth/2,-this.height/2-this.strokeWidth/2),t.scale(o*this.scaleX/n.zoomX,o*this.scaleY/n.zoomY),t.strokeStyle=r.createPattern(s,"no-repeat")},_findCenterFromElement:function(){return{x:this.left+this.width/2,y:this.top+this.height/2}},_assignTransformMatrixProps:function(){if(this.transformMatrix){var t=e.util.qrDecompose(this.transformMatrix);this.flipX=!1,this.flipY=!1,this.set("scaleX",t.scaleX),this.set("scaleY",t.scaleY),this.angle=t.angle,this.skewX=t.skewX,this.skewY=0}},_removeTransformMatrix:function(t){var i=this._findCenterFromElement();this.transformMatrix&&(this._assignTransformMatrixProps(),i=e.util.transformPoint(i,this.transformMatrix)),this.transformMatrix=null,t&&(this.scaleX*=t.scaleX,this.scaleY*=t.scaleY,this.cropX=t.cropX,this.cropY=t.cropY,i.x+=t.offsetLeft,i.y+=t.offsetTop,this.width=t.width,this.height=t.height),this.setPositionByOrigin(i,"center","center")},clone:function(t,i){var r=this.toObject(i);this.constructor.fromObject?this.constructor.fromObject(r,t):e.Object._fromObject("Object",r,t)},cloneAsImage:function(t,i){var r=this.toCanvasElement(i);return t&&t(new e.Image(r)),this},toCanvasElement:function(t){t||(t={});var i=e.util,r=i.saveObjectTransform(this),n=this.group,s=this.shadow,o=Math.abs,a=(t.multiplier||1)*(t.enableRetinaScaling?e.devicePixelRatio:1);delete this.group,t.withoutTransform&&i.resetObjectTransform(this),t.withoutShadow&&(this.shadow=null);var c,h,l,u,f=e.util.createCanvasElement(),d=this.getBoundingRect(!0,!0),g=this.shadow,p={x:0,y:0};g&&(h=g.blur,c=g.nonScaling?{scaleX:1,scaleY:1}:this.getObjectScaling(),p.x=2*Math.round(o(g.offsetX)+h)*o(c.scaleX),p.y=2*Math.round(o(g.offsetY)+h)*o(c.scaleY)),l=d.width+p.x,u=d.height+p.y,f.width=Math.ceil(l),f.height=Math.ceil(u);var v=new e.StaticCanvas(f,{enableRetinaScaling:!1,renderOnAddRemove:!1,skipOffscreen:!1});"jpeg"===t.format&&(v.backgroundColor="#fff"),this.setPositionByOrigin(new e.Point(v.width/2,v.height/2),"center","center");var m=this.canvas;v.add(this);var b=v.toCanvasElement(a||1,t);return this.shadow=s,this.set("canvas",m),n&&(this.group=n),this.set(r).setCoords(),v._objects=[],v.dispose(),v=null,b},toDataURL:function(t){return t||(t={}),e.util.toDataURL(this.toCanvasElement(t),t.format||"png",t.quality||1)},isType:function(t){return arguments.length>1?Array.from(arguments).includes(this.type):this.type===t},complexity:function(){return 1},toJSON:function(t){return this.toObject(t)},rotate:function(t){var e=("center"!==this.originX||"center"!==this.originY)&&this.centeredRotation;return e&&this._setOriginToCenter(),this.set("angle",t),e&&this._resetOrigin(),this},centerH:function(){return this.canvas&&this.canvas.centerObjectH(this),this},viewportCenterH:function(){return this.canvas&&this.canvas.viewportCenterObjectH(this),this},centerV:function(){return this.canvas&&this.canvas.centerObjectV(this),this},viewportCenterV:function(){return this.canvas&&this.canvas.viewportCenterObjectV(this),this},center:function(){return this.canvas&&this.canvas.centerObject(this),this},viewportCenter:function(){return this.canvas&&this.canvas.viewportCenterObject(this),this},getLocalPointer:function(t,i){i=i||this.canvas.getPointer(t);var r=new e.Point(i.x,i.y),n=this._getLeftTopCoords();return this.angle&&(r=e.util.rotatePoint(r,n,o(-this.angle))),{x:r.x-n.x,y:r.y-n.y}},_setupCompositeOperation:function(t){this.globalCompositeOperation&&(t.globalCompositeOperation=this.globalCompositeOperation)},dispose:function(){e.runningAnimations&&e.runningAnimations.cancelByTarget(this)}}),e.util.createAccessors&&e.util.createAccessors(e.Object),i(e.Object.prototype,e.Observable),e.Object.NUM_FRACTION_DIGITS=2,e.Object.ENLIVEN_PROPS=["clipPath"],e.Object._fromObject=function(t,i,n,s){var o=e[t];i=r(i,!0),e.util.enlivenPatterns([i.fill,i.stroke],function(t){"undefined"!=typeof t[0]&&(i.fill=t[0]),"undefined"!=typeof t[1]&&(i.stroke=t[1]),e.util.enlivenObjectEnlivables(i,i,function(){var t=s?new o(i[s],i):new o(i);n&&n(t)})})},e.Object.__uid=0)}("undefined"!=typeof exports?exports:this);!function(){var t=fabric.util.degreesToRadians,e={left:-.5,center:0,right:.5},i={top:-.5,center:0,bottom:.5};fabric.util.object.extend(fabric.Object.prototype,{translateToGivenOrigin:function(t,r,n,s,o){var a,c,h,l=t.x,u=t.y;return"string"==typeof r?r=e[r]:r-=.5,"string"==typeof s?s=e[s]:s-=.5,a=s-r,"string"==typeof n?n=i[n]:n-=.5,"string"==typeof o?o=i[o]:o-=.5,c=o-n,(a||c)&&(h=this._getTransformedDimensions(),l=t.x+a*h.x,u=t.y+c*h.y),new fabric.Point(l,u)},translateToCenterPoint:function(e,i,r){var n=this.translateToGivenOrigin(e,i,r,"center","center");return this.angle?fabric.util.rotatePoint(n,e,t(this.angle)):n},translateToOriginPoint:function(e,i,r){var n=this.translateToGivenOrigin(e,"center","center",i,r);return this.angle?fabric.util.rotatePoint(n,e,t(this.angle)):n},getCenterPoint:function(){var t=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(t,this.originX,this.originY)},getPointByOrigin:function(t,e){var i=this.getCenterPoint();return this.translateToOriginPoint(i,t,e)},toLocalPoint:function(e,i,r){var n,s,o=this.getCenterPoint();return n="undefined"!=typeof i&&"undefined"!=typeof r?this.translateToGivenOrigin(o,"center","center",i,r):new fabric.Point(this.left,this.top),s=new fabric.Point(e.x,e.y),this.angle&&(s=fabric.util.rotatePoint(s,o,-t(this.angle))),s.subtractEquals(n)},setPositionByOrigin:function(t,e,i){var r=this.translateToCenterPoint(t,e,i),n=this.translateToOriginPoint(r,this.originX,this.originY);this.set("left",n.x),this.set("top",n.y)},adjustPosition:function(i){var r,n,s=t(this.angle),o=this.getScaledWidth(),a=fabric.util.cos(s)*o,c=fabric.util.sin(s)*o;r="string"==typeof this.originX?e[this.originX]:this.originX-.5,n="string"==typeof i?e[i]:i-.5,this.left+=a*(n-r),this.top+=c*(n-r),this.setCoords(),this.originX=i},_setOriginToCenter:function(){this._originalOriginX=this.originX,this._originalOriginY=this.originY;var t=this.getCenterPoint();this.originX="center",this.originY="center",this.left=t.x,this.top=t.y},_resetOrigin:function(){var t=this.translateToOriginPoint(this.getCenterPoint(),this._originalOriginX,this._originalOriginY);this.originX=this._originalOriginX,this.originY=this._originalOriginY,this.left=t.x,this.top=t.y,this._originalOriginX=null,this._originalOriginY=null},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","top")}})}();!function(){function t(t){return[new fabric.Point(t.tl.x,t.tl.y),new fabric.Point(t.tr.x,t.tr.y),new fabric.Point(t.br.x,t.br.y),new fabric.Point(t.bl.x,t.bl.y)]}var e=fabric.util,i=e.degreesToRadians,r=e.multiplyTransformMatrices,n=e.transformPoint;e.object.extend(fabric.Object.prototype,{oCoords:null,aCoords:null,lineCoords:null,ownMatrixCache:null,matrixCache:null,controls:{},_getCoords:function(t,e){return e?t?this.calcACoords():this.calcLineCoords():(this.aCoords&&this.lineCoords||this.setCoords(!0),t?this.aCoords:this.lineCoords)},getCoords:function(e,i){return t(this._getCoords(e,i))},intersectsWithRect:function(t,e,i,r){var n=this.getCoords(i,r),s=fabric.Intersection.intersectPolygonRectangle(n,t,e);return"Intersection"===s.status},intersectsWithObject:function(t,e,i){var r=fabric.Intersection.intersectPolygonPolygon(this.getCoords(e,i),t.getCoords(e,i));return"Intersection"===r.status||t.isContainedWithinObject(this,e,i)||this.isContainedWithinObject(t,e,i)},isContainedWithinObject:function(t,e,i){for(var r=this.getCoords(e,i),n=e?t.aCoords:t.lineCoords,s=0,o=t._getImageLines(n);4>s;s++)if(!t.containsPoint(r[s],o))return!1;return!0},isContainedWithinRect:function(t,e,i,r){var n=this.getBoundingRect(i,r);return n.left>=t.x&&n.left+n.width<=e.x&&n.top>=t.y&&n.top+n.height<=e.y},containsPoint:function(t,e,i,r){var n=this._getCoords(i,r),e=e||this._getImageLines(n),s=this._findCrossPoints(t,e);return 0!==s&&s%2===1},isOnScreen:function(t){if(!this.canvas)return!1;var e=this.canvas.vptCoords.tl,i=this.canvas.vptCoords.br,r=this.getCoords(!0,t);return r.some(function(t){return t.x<=i.x&&t.x>=e.x&&t.y<=i.y&&t.y>=e.y})?!0:this.intersectsWithRect(e,i,!0,t)?!0:this._containsCenterOfCanvas(e,i,t)},_containsCenterOfCanvas:function(t,e,i){var r={x:(t.x+e.x)/2,y:(t.y+e.y)/2};return this.containsPoint(r,null,!0,i)?!0:!1},isPartiallyOnScreen:function(t){if(!this.canvas)return!1;var e=this.canvas.vptCoords.tl,i=this.canvas.vptCoords.br;if(this.intersectsWithRect(e,i,!0,t))return!0;var r=this.getCoords(!0,t).every(function(t){return(t.x>=i.x||t.x<=e.x)&&(t.y>=i.y||t.y<=e.y)});return r&&this._containsCenterOfCanvas(e,i,t)},_getImageLines:function(t){var e={topline:{o:t.tl,d:t.tr},rightline:{o:t.tr,d:t.br},bottomline:{o:t.br,d:t.bl},leftline:{o:t.bl,d:t.tl}};return e},_findCrossPoints:function(t,e){var i,r,n,s,o,a,c=0;for(var h in e)if(a=e[h],!(a.o.y=t.y&&a.d.y>=t.y||(a.o.x===a.d.x&&a.o.x>=t.x?o=a.o.x:(i=0,r=(a.d.y-a.o.y)/(a.d.x-a.o.x),n=t.y-i*t.x,s=a.o.y-r*a.o.x,o=-(n-s)/(i-r)),o>=t.x&&(c+=1),2!==c)))break;return c},getBoundingRect:function(t,i){var r=this.getCoords(t,i);return e.makeBoundingBoxFromPoints(r)},getScaledWidth:function(){return this._getTransformedDimensions().x},getScaledHeight:function(){return this._getTransformedDimensions().y},_constrainScale:function(t){return Math.abs(t)t?-this.minScaleLimit:this.minScaleLimit:0===t?1e-4:t},scale:function(t){return this._set("scaleX",t),this._set("scaleY",t),this.setCoords()},scaleToWidth:function(t,e){var i=this.getBoundingRect(e).width/this.getScaledWidth();return this.scale(t/this.width/i)},scaleToHeight:function(t,e){var i=this.getBoundingRect(e).height/this.getScaledHeight();return this.scale(t/this.height/i)},calcLineCoords:function(){var t=this.getViewportTransform(),r=this.padding,s=i(this.angle),o=e.cos(s),a=e.sin(s),c=o*r,h=a*r,l=c+h,u=c-h,f=this.calcACoords(),d={tl:n(f.tl,t),tr:n(f.tr,t),bl:n(f.bl,t),br:n(f.br,t)};return r&&(d.tl.x-=u,d.tl.y-=l,d.tr.x+=l,d.tr.y-=u,d.bl.x-=l,d.bl.y+=u,d.br.x+=u,d.br.y+=l),d},calcOCoords:function(){var t=this._calcRotateMatrix(),e=this._calcTranslateMatrix(),i=this.getViewportTransform(),n=r(i,e),s=r(n,t),s=r(s,[1/i[0],0,0,1/i[3],0,0]),o=this._calculateCurrentDimensions(),a={};return this.forEachControl(function(t,e,i){a[e]=t.positionHandler(o,s,i)}),a},calcACoords:function(){var t=this._calcRotateMatrix(),e=this._calcTranslateMatrix(),i=r(e,t),s=this._getTransformedDimensions(),o=s.x/2,a=s.y/2;return{tl:n({x:-o,y:-a},i),tr:n({x:o,y:-a},i),bl:n({x:-o,y:a},i),br:n({x:o,y:a},i)}},setCoords:function(t){return this.aCoords=this.calcACoords(),this.lineCoords=this.group?this.aCoords:this.calcLineCoords(),t?this:(this.oCoords=this.calcOCoords(),this._setCornerCoords&&this._setCornerCoords(),this)},_calcRotateMatrix:function(){return e.calcRotateMatrix(this)},_calcTranslateMatrix:function(){var t=this.getCenterPoint();return[1,0,0,1,t.x,t.y]},transformMatrixKey:function(t){var e="_",i="";return!t&&this.group&&(i=this.group.transformMatrixKey(t)+e),i+this.top+e+this.left+e+this.scaleX+e+this.scaleY+e+this.skewX+e+this.skewY+e+this.angle+e+this.originX+e+this.originY+e+this.width+e+this.height+e+this.strokeWidth+this.flipX+this.flipY},calcTransformMatrix:function(t){var e=this.calcOwnMatrix();if(t||!this.group)return e;var i=this.transformMatrixKey(t),n=this.matrixCache||(this.matrixCache={});return n.key===i?n.value:(this.group&&(e=r(this.group.calcTransformMatrix(!1),e)),n.key=i,n.value=e,e)},calcOwnMatrix:function(){var t=this.transformMatrixKey(!0),i=this.ownMatrixCache||(this.ownMatrixCache={});if(i.key===t)return i.value;var r=this._calcTranslateMatrix(),n={angle:this.angle,translateX:r[4],translateY:r[5],scaleX:this.scaleX,scaleY:this.scaleY,skewX:this.skewX,skewY:this.skewY,flipX:this.flipX,flipY:this.flipY};return i.key=t,i.value=e.composeMatrix(n),i.value},_getNonTransformedDimensions:function(){var t=this.strokeWidth,e=this.width+t,i=this.height+t;return{x:e,y:i}},_getTransformedDimensions:function(t,i){"undefined"==typeof t&&(t=this.skewX),"undefined"==typeof i&&(i=this.skewY);var r,n,s,o=0===t&&0===i;if(this.strokeUniform?(n=this.width,s=this.height):(r=this._getNonTransformedDimensions(),n=r.x,s=r.y),o)return this._finalizeDimensions(n*this.scaleX,s*this.scaleY);var a=e.sizeAfterTransform(n,s,{scaleX:this.scaleX,scaleY:this.scaleY,skewX:t,skewY:i});return this._finalizeDimensions(a.x,a.y)},_finalizeDimensions:function(t,e){return this.strokeUniform?{x:t+this.strokeWidth,y:e+this.strokeWidth}:{x:t,y:e}},_calculateCurrentDimensions:function(){var t=this.getViewportTransform(),e=this._getTransformedDimensions(),i=n(e,t,!0);return i.scalarAdd(2*this.padding)}})}();fabric.util.object.extend(fabric.Object.prototype,{sendToBack:function(){return this.group?fabric.StaticCanvas.prototype.sendToBack.call(this.group,this):this.canvas&&this.canvas.sendToBack(this),this},bringToFront:function(){return this.group?fabric.StaticCanvas.prototype.bringToFront.call(this.group,this):this.canvas&&this.canvas.bringToFront(this),this},sendBackwards:function(t){return this.group?fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,t):this.canvas&&this.canvas.sendBackwards(this,t),this},bringForward:function(t){return this.group?fabric.StaticCanvas.prototype.bringForward.call(this.group,this,t):this.canvas&&this.canvas.bringForward(this,t),this},moveTo:function(t){return this.group&&"activeSelection"!==this.group.type?fabric.StaticCanvas.prototype.moveTo.call(this.group,this,t):this.canvas&&this.canvas.moveTo(this,t),this}});!function(){function t(t,e){if(e){if(e.toLive)return t+": url(#SVGID_"+e.id+"); ";var i=new fabric.Color(e),r=t+": "+i.toRgb()+"; ",n=i.getAlpha();return 1!==n&&(r+=t+"-opacity: "+n.toString()+"; "),r}return t+": none; "}var e=fabric.util.toFixed;fabric.util.object.extend(fabric.Object.prototype,{getSvgStyles:function(e){var i=this.fillRule?this.fillRule:"nonzero",r=this.strokeWidth?this.strokeWidth:"0",n=this.strokeDashArray?this.strokeDashArray.join(" "):"none",s=this.strokeDashOffset?this.strokeDashOffset:"0",o=this.strokeLineCap?this.strokeLineCap:"butt",a=this.strokeLineJoin?this.strokeLineJoin:"miter",c=this.strokeMiterLimit?this.strokeMiterLimit:"4",h="undefined"!=typeof this.opacity?this.opacity:"1",l=this.visible?"":" visibility: hidden;",u=e?"":this.getSvgFilter(),f=t("fill",this.fill),d=t("stroke",this.stroke);return[d,"stroke-width: ",r,"; ","stroke-dasharray: ",n,"; ","stroke-linecap: ",o,"; ","stroke-dashoffset: ",s,"; ","stroke-linejoin: ",a,"; ","stroke-miterlimit: ",c,"; ",f,"fill-rule: ",i,"; ","opacity: ",h,";",u,l].join("")},getSvgSpanStyles:function(e,i){var r="; ",n=e.fontFamily?"font-family: "+(-1===e.fontFamily.indexOf("'")&&-1===e.fontFamily.indexOf('"')?"'"+e.fontFamily+"'":e.fontFamily)+r:"",s=e.strokeWidth?"stroke-width: "+e.strokeWidth+r:"",n=n,o=e.fontSize?"font-size: "+e.fontSize+"px"+r:"",a=e.fontStyle?"font-style: "+e.fontStyle+r:"",c=e.fontWeight?"font-weight: "+e.fontWeight+r:"",h=e.fill?t("fill",e.fill):"",l=e.stroke?t("stroke",e.stroke):"",u=this.getSvgTextDecoration(e),f=e.deltaY?"baseline-shift: "+-e.deltaY+"; ":"";return u&&(u="text-decoration: "+u+r),[l,s,n,o,a,c,u,h,f,i?"white-space: pre; ":""].join("")},getSvgTextDecoration:function(t){return["overline","underline","line-through"].filter(function(e){return t[e.replace("-","")]}).join(" ")},getSvgFilter:function(){return this.shadow?"filter: url(#SVGID_"+this.shadow.id+");":""},getSvgCommons:function(){return[this.id?'id="'+this.id+'" ':"",this.clipPath?'clip-path="url(#'+this.clipPath.clipPathId+')" ':""].join("")},getSvgTransform:function(t,e){var i=t?this.calcTransformMatrix():this.calcOwnMatrix(),r='transform="'+fabric.util.matrixToSVG(i);return r+(e||"")+'" '},_setSVGBg:function(t){if(this.backgroundColor){var i=fabric.Object.NUM_FRACTION_DIGITS;t.push(" \n')}},toSVG:function(t){return this._createBaseSVGMarkup(this._toSVG(t),{reviver:t})},toClipPathSVG:function(t){return" "+this._createBaseClipPathSVGMarkup(this._toSVG(t),{reviver:t})},_createBaseClipPathSVGMarkup:function(t,e){e=e||{};var i=e.reviver,r=e.additionalTransform||"",n=[this.getSvgTransform(!0,r),this.getSvgCommons()].join(""),s=t.indexOf("COMMON_PARTS");return t[s]=n,i?i(t.join("")):t.join("")},_createBaseSVGMarkup:function(t,e){e=e||{};var i,r,n=e.noStyle,s=e.reviver,o=n?"":'style="'+this.getSvgStyles()+'" ',a=e.withShadow?'style="'+this.getSvgFilter()+'" ':"",c=this.clipPath,h=this.strokeUniform?'vector-effect="non-scaling-stroke" ':"",l=c&&c.absolutePositioned,u=this.stroke,f=this.fill,d=this.shadow,g=[],p=t.indexOf("COMMON_PARTS"),v=e.additionalTransform;return c&&(c.clipPathId="CLIPPATH_"+fabric.Object.__uid++,r='\n'+c.toClipPathSVG(s)+"\n"),l&&g.push("\n"),g.push("\n"),i=[o,h,n?"":this.addPaintOrder()," ",v?'transform="'+v+'" ':""].join(""),t[p]=i,f&&f.toLive&&g.push(f.toSVG(this)),u&&u.toLive&&g.push(u.toSVG(this)),d&&g.push(d.toSVG(this)),c&&g.push(r),g.push(t.join("")),g.push("\n"),l&&g.push("\n"),s?s(g.join("")):g.join("")},addPaintOrder:function(){return"fill"!==this.paintFirst?' paint-order="'+this.paintFirst+'" ':""}})}();!function(){function t(t,e,r){var n={},s=!0;r.forEach(function(e){n[e]=t[e]}),i(t[e],n,s)}function e(t,i,r){if(t===i)return!0;if(Array.isArray(t)){if(!Array.isArray(i)||t.length!==i.length)return!1;for(var n=0,s=t.length;s>n;n++)if(!e(t[n],i[n]))return!1;return!0}if(t&&"object"==typeof t){var o,a=Object.keys(t);if(!i||"object"!=typeof i||!r&&a.length!==Object.keys(i).length)return!1;for(var n=0,s=a.length;s>n;n++)if(o=a[n],"canvas"!==o&&"group"!==o&&!e(t[o],i[o]))return!1;return!0}}var i=fabric.util.object.extend,r="stateProperties";fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(t){t=t||r;var i="_"+t;return Object.keys(this[i]).length=0;c--)if(n=a[c],this.isControlVisible(n)&&(r=this._getImageLines(e?this.oCoords[n].touchCorner:this.oCoords[n].corner),i=this._findCrossPoints({x:s,y:o},r),0!==i&&i%2===1))return this.__corner=n,n;return!1},forEachControl:function(t){for(var e in this.controls)t(this.controls[e],e,this)},_setCornerCoords:function(){var t=this.oCoords;for(var e in t){var i=this.controls[e];t[e].corner=i.calcCornerCoords(this.angle,this.cornerSize,t[e].x,t[e].y,!1),t[e].touchCorner=i.calcCornerCoords(this.angle,this.touchCornerSize,t[e].x,t[e].y,!0)}},drawSelectionBackground:function(e){if(!this.selectionBackgroundColor||this.canvas&&!this.canvas.interactive||this.canvas&&this.canvas._activeObject!==this)return this;e.save();var i=this.getCenterPoint(),r=this._calculateCurrentDimensions(),n=this.canvas.viewportTransform;return e.translate(i.x,i.y),e.scale(1/n[0],1/n[3]),e.rotate(t(this.angle)),e.fillStyle=this.selectionBackgroundColor,e.fillRect(-r.x/2,-r.y/2,r.x,r.y),e.restore(),this},drawBorders:function(t,e){e=e||{};var i=this._calculateCurrentDimensions(),r=this.borderScaleFactor,n=i.x+r,s=i.y+r,o="undefined"!=typeof e.hasControls?e.hasControls:this.hasControls,a=!1;return t.save(),t.strokeStyle=e.borderColor||this.borderColor,this._setLineDash(t,e.borderDashArray||this.borderDashArray),t.strokeRect(-n/2,-s/2,n,s),o&&(t.beginPath(),this.forEachControl(function(e,i,r){e.withConnection&&e.getVisibility(r,i)&&(a=!0,t.moveTo(e.x*n,e.y*s),t.lineTo(e.x*n+e.offsetX,e.y*s+e.offsetY))}),a&&t.stroke()),t.restore(),this},drawBordersInGroup:function(t,e,i){i=i||{};var r=fabric.util.sizeAfterTransform(this.width,this.height,e),n=this.strokeWidth,s=this.strokeUniform,o=this.borderScaleFactor,a=r.x+n*(s?this.canvas.getZoom():e.scaleX)+o,c=r.y+n*(s?this.canvas.getZoom():e.scaleY)+o;return t.save(),this._setLineDash(t,i.borderDashArray||this.borderDashArray),t.strokeStyle=i.borderColor||this.borderColor,t.strokeRect(-a/2,-c/2,a,c),t.restore(),this},drawControls:function(t,e){e=e||{},t.save();var i,r,n=this.canvas.getRetinaScaling();return t.setTransform(n,0,0,n,0,0),t.strokeStyle=t.fillStyle=e.cornerColor||this.cornerColor,this.transparentCorners||(t.strokeStyle=e.cornerStrokeColor||this.cornerStrokeColor),this._setLineDash(t,e.cornerDashArray||this.cornerDashArray),this.setCoords(),this.group&&(i=this.group.calcTransformMatrix()),this.forEachControl(function(n,s,o){r=o.oCoords[s],n.getVisibility(o,s)&&(i&&(r=fabric.util.transformPoint(r,i)),n.render(t,r.x,r.y,e,o))}),t.restore(),this},isControlVisible:function(t){return this.controls[t]&&this.controls[t].getVisibility(this,t)},setControlVisible:function(t,e){return this._controlsVisibility||(this._controlsVisibility={}),this._controlsVisibility[t]=e,this},setControlsVisibility:function(t){t||(t={});for(var e in t)this.setControlVisible(e,t[e]);return this},onDeselect:function(){},onSelect:function(){}})}();fabric.util.object.extend(fabric.StaticCanvas.prototype,{FX_DURATION:500,fxCenterObjectH:function(t,e){e=e||{};var i=function(){},r=e.onComplete||i,n=e.onChange||i,s=this;return fabric.util.animate({target:this,startValue:t.left,endValue:this.getCenterPoint().x,duration:this.FX_DURATION,onChange:function(e){t.set("left",e),s.requestRenderAll(),n()},onComplete:function(){t.setCoords(),r()}})},fxCenterObjectV:function(t,e){e=e||{};var i=function(){},r=e.onComplete||i,n=e.onChange||i,s=this;return fabric.util.animate({target:this,startValue:t.top,endValue:this.getCenterPoint().y,duration:this.FX_DURATION,onChange:function(e){t.set("top",e),s.requestRenderAll(),n()},onComplete:function(){t.setCoords(),r()}})},fxRemove:function(t,e){e=e||{};var i=function(){},r=e.onComplete||i,n=e.onChange||i,s=this;return fabric.util.animate({target:this,startValue:t.opacity,endValue:0,duration:this.FX_DURATION,onChange:function(e){t.set("opacity",e),s.requestRenderAll(),n()},onComplete:function(){s.remove(t),r()}})}}),fabric.util.object.extend(fabric.Object.prototype,{animate:function(){if(arguments[0]&&"object"==typeof arguments[0]){var t,e,i=[],r=[];for(t in arguments[0])i.push(t);for(var n=0,s=i.length;s>n;n++)t=i[n],e=n!==s-1,r.push(this._animate(t,arguments[0][t],arguments[1],e));return r}return this._animate.apply(this,arguments)},_animate:function(t,e,i,r){var n,s=this;e=e.toString(),i=i?fabric.util.object.clone(i):{},~t.indexOf(".")&&(n=t.split("."));var o=s.colorProperties.indexOf(t)>-1||n&&s.colorProperties.indexOf(n[1])>-1,a=n?this.get(n[0])[n[1]]:this.get(t);"from"in i||(i.from=a),o||(e=~e.indexOf("=")?a+parseFloat(e.replace("=","")):parseFloat(e));var c={target:this,startValue:i.from,endValue:e,byValue:i.by,easing:i.easing,duration:i.duration,abort:i.abort&&function(t,e,r){return i.abort.call(s,t,e,r)},onChange:function(e,o,a){n?s[n[0]][n[1]]=e:s.set(t,e),r||i.onChange&&i.onChange(e,o,a)},onComplete:function(t,e,n){r||(s.setCoords(),i.onComplete&&i.onComplete(t,e,n))}};return o?fabric.util.animateColor(c.startValue,c.endValue,c.duration,c):fabric.util.animate(c)}});!function(t){"use strict";function e(t,e){var i=t.origin,r=t.axis1,n=t.axis2,s=t.dimension,o=e.nearest,a=e.center,c=e.farthest;return function(){switch(this.get(i)){case o:return Math.min(this.get(r),this.get(n));case a:return Math.min(this.get(r),this.get(n))+.5*this.get(s);case c:return Math.max(this.get(r),this.get(n))}}}var i=t.fabric||(t.fabric={}),r=i.util.object.extend,n=i.util.object.clone,s={x1:1,x2:1,y1:1,y2:1};return i.Line?void i.warn("fabric.Line is already defined"):(i.Line=i.util.createClass(i.Object,{type:"line",x1:0,y1:0,x2:0,y2:0,cacheProperties:i.Object.prototype.cacheProperties.concat("x1","x2","y1","y2"),initialize:function(t,e){t||(t=[0,0,0,0]),this.callSuper("initialize",e),this.set("x1",t[0]),this.set("y1",t[1]),this.set("x2",t[2]),this.set("y2",t[3]),this._setWidthHeight(e)},_setWidthHeight:function(t){t||(t={}),this.width=Math.abs(this.x2-this.x1),this.height=Math.abs(this.y2-this.y1),this.left="left"in t?t.left:this._getLeftToOriginX(),this.top="top"in t?t.top:this._getTopToOriginY()},_set:function(t,e){return this.callSuper("_set",t,e),"undefined"!=typeof s[t]&&this._setWidthHeight(),this},_getLeftToOriginX:e({origin:"originX",axis1:"x1",axis2:"x2",dimension:"width"},{nearest:"left",center:"center",farthest:"right"}),_getTopToOriginY:e({origin:"originY",axis1:"y1",axis2:"y2",dimension:"height"},{nearest:"top",center:"center",farthest:"bottom"}),_render:function(t){t.beginPath();var e=this.calcLinePoints();t.moveTo(e.x1,e.y1),t.lineTo(e.x2,e.y2),t.lineWidth=this.strokeWidth;var i=t.strokeStyle;t.strokeStyle=this.stroke||t.fillStyle,this.stroke&&this._renderStroke(t),t.strokeStyle=i},_findCenterFromElement:function(){return{x:(this.x1+this.x2)/2,y:(this.y1+this.y2)/2}},toObject:function(t){return r(this.callSuper("toObject",t),this.calcLinePoints())},_getNonTransformedDimensions:function(){var t=this.callSuper("_getNonTransformedDimensions");return"butt"===this.strokeLineCap&&(0===this.width&&(t.y-=this.strokeWidth),0===this.height&&(t.x-=this.strokeWidth)),t},calcLinePoints:function(){var t=this.x1<=this.x2?-1:1,e=this.y1<=this.y2?-1:1,i=t*this.width*.5,r=e*this.height*.5,n=t*this.width*-.5,s=e*this.height*-.5;return{x1:i,x2:n,y1:r,y2:s}},_toSVG:function(){var t=this.calcLinePoints();return["\n']}}),i.Line.ATTRIBUTE_NAMES=i.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),i.Line.fromElement=function(t,e,n){n=n||{};var s=i.parseAttributes(t,i.Line.ATTRIBUTE_NAMES),o=[s.x1||0,s.y1||0,s.x2||0,s.y2||0];e(new i.Line(o,r(s,n)))},void(i.Line.fromObject=function(t,e){function r(t){delete t.points,e&&e(t)}var s=n(t,!0);s.points=[t.x1,t.y1,t.x2,t.y2],i.Object._fromObject("Line",s,r,"points")}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";function e(t){return"radius"in t&&t.radius>=0}var i=t.fabric||(t.fabric={}),r=i.util.degreesToRadians;return i.Circle?void i.warn("fabric.Circle is already defined."):(i.Circle=i.util.createClass(i.Object,{type:"circle",radius:0,startAngle:0,endAngle:360,cacheProperties:i.Object.prototype.cacheProperties.concat("radius","startAngle","endAngle"),_set:function(t,e){return this.callSuper("_set",t,e),"radius"===t&&this.setRadius(e),this},toObject:function(t){return this.callSuper("toObject",["radius","startAngle","endAngle"].concat(t))},_toSVG:function(){var t,e=0,n=0,s=(this.endAngle-this.startAngle)%360;if(0===s)t=["\n'];else{var o=r(this.startAngle),a=r(this.endAngle),c=this.radius,h=i.util.cos(o)*c,l=i.util.sin(o)*c,u=i.util.cos(a)*c,f=i.util.sin(a)*c,d=s>180?"1":"0";t=['\n"]}return t},_render:function(t){t.beginPath(),t.arc(0,0,this.radius,r(this.startAngle),r(this.endAngle),!1),this._renderPaintInOrder(t)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(t){return this.radius=t,this.set("width",2*t).set("height",2*t)}}),i.Circle.ATTRIBUTE_NAMES=i.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),i.Circle.fromElement=function(t,r){var n=i.parseAttributes(t,i.Circle.ATTRIBUTE_NAMES);if(!e(n))throw new Error("value of `r` attribute is required and can not be negative");n.left=(n.left||0)-n.radius,n.top=(n.top||0)-n.radius,r(new i.Circle(n))},void(i.Circle.fromObject=function(t,e){i.Object._fromObject("Circle",t,e)}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={});return e.Triangle?void e.warn("fabric.Triangle is already defined"):(e.Triangle=e.util.createClass(e.Object,{type:"triangle",width:100,height:100,_render:function(t){var e=this.width/2,i=this.height/2;t.beginPath(),t.moveTo(-e,i),t.lineTo(0,-i),t.lineTo(e,i),t.closePath(),this._renderPaintInOrder(t)},_toSVG:function(){var t=this.width/2,e=this.height/2,i=[-t+" "+e,"0 "+-e,t+" "+e].join(",");return["']}}),void(e.Triangle.fromObject=function(t,i){return e.Object._fromObject("Triangle",t,i)}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=2*Math.PI;return e.Ellipse?void e.warn("fabric.Ellipse is already defined."):(e.Ellipse=e.util.createClass(e.Object,{type:"ellipse",rx:0,ry:0,cacheProperties:e.Object.prototype.cacheProperties.concat("rx","ry"),initialize:function(t){this.callSuper("initialize",t),this.set("rx",t&&t.rx||0),this.set("ry",t&&t.ry||0)},_set:function(t,e){switch(this.callSuper("_set",t,e),t){case"rx":this.rx=e,this.set("width",2*e);break;case"ry":this.ry=e,this.set("height",2*e)}return this},getRx:function(){return this.get("rx")*this.get("scaleX")},getRy:function(){return this.get("ry")*this.get("scaleY")},toObject:function(t){return this.callSuper("toObject",["rx","ry"].concat(t))},_toSVG:function(){return["\n']},_render:function(t){t.beginPath(),t.save(),t.transform(1,0,0,this.ry/this.rx,0,0),t.arc(0,0,this.rx,0,i,!1),t.restore(),this._renderPaintInOrder(t)}}),e.Ellipse.ATTRIBUTE_NAMES=e.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),e.Ellipse.fromElement=function(t,i){var r=e.parseAttributes(t,e.Ellipse.ATTRIBUTE_NAMES);r.left=(r.left||0)-r.rx,r.top=(r.top||0)-r.ry,i(new e.Ellipse(r))},void(e.Ellipse.fromObject=function(t,i){e.Object._fromObject("Ellipse",t,i)}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend;return e.Rect?void e.warn("fabric.Rect is already defined"):(e.Rect=e.util.createClass(e.Object,{stateProperties:e.Object.prototype.stateProperties.concat("rx","ry"),type:"rect",rx:0,ry:0,cacheProperties:e.Object.prototype.cacheProperties.concat("rx","ry"),initialize:function(t){this.callSuper("initialize",t),this._initRxRy()},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(t){var e=this.rx?Math.min(this.rx,this.width/2):0,i=this.ry?Math.min(this.ry,this.height/2):0,r=this.width,n=this.height,s=-this.width/2,o=-this.height/2,a=0!==e||0!==i,c=.4477152502;t.beginPath(),t.moveTo(s+e,o),t.lineTo(s+r-e,o),a&&t.bezierCurveTo(s+r-c*e,o,s+r,o+c*i,s+r,o+i),t.lineTo(s+r,o+n-i),a&&t.bezierCurveTo(s+r,o+n-c*i,s+r-c*e,o+n,s+r-e,o+n),t.lineTo(s+e,o+n),a&&t.bezierCurveTo(s+c*e,o+n,s,o+n-c*i,s,o+n-i),t.lineTo(s,o+i),a&&t.bezierCurveTo(s,o+c*i,s+c*e,o,s+e,o),t.closePath(),this._renderPaintInOrder(t)},toObject:function(t){return this.callSuper("toObject",["rx","ry"].concat(t))},_toSVG:function(){var t=-this.width/2,e=-this.height/2;return["\n']}}),e.Rect.ATTRIBUTE_NAMES=e.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),e.Rect.fromElement=function(t,r,n){if(!t)return r(null);n=n||{};var s=e.parseAttributes(t,e.Rect.ATTRIBUTE_NAMES);s.left=s.left||0,s.top=s.top||0,s.height=s.height||0,s.width=s.width||0;var o=new e.Rect(i(n?e.util.object.clone(n):{},s));o.visible=o.visible&&o.width>0&&o.height>0,r(o)},void(e.Rect.fromObject=function(t,i){return e.Object._fromObject("Rect",t,i)}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend,r=e.util.array.min,n=e.util.array.max,s=e.util.toFixed,o=e.util.projectStrokeOnPoints;return e.Polyline?void e.warn("fabric.Polyline is already defined"):(e.Polyline=e.util.createClass(e.Object,{type:"polyline",points:null,exactBoundingBox:!1,cacheProperties:e.Object.prototype.cacheProperties.concat("points"),initialize:function(t,e){e=e||{},this.points=t||[],this.callSuper("initialize",e),this._setPositionDimensions(e)},_projectStrokeOnPoints:function(){return o(this.points,this,!0)},_setPositionDimensions:function(t){var e,i=this._calcDimensions(t),r=this.exactBoundingBox?this.strokeWidth:0;this.width=i.width-r,this.height=i.height-r,t.fromSVG||(e=this.translateToGivenOrigin({x:i.left-this.strokeWidth/2+r/2,y:i.top-this.strokeWidth/2+r/2},"left","top",this.originX,this.originY)),"undefined"==typeof t.left&&(this.left=t.fromSVG?i.left:e.x),"undefined"==typeof t.top&&(this.top=t.fromSVG?i.top:e.y),this.pathOffset={x:i.left+this.width/2+r/2,y:i.top+this.height/2+r/2}},_calcDimensions:function(){var t=this.exactBoundingBox?this._projectStrokeOnPoints():this.points,e=r(t,"x")||0,i=r(t,"y")||0,s=n(t,"x")||0,o=n(t,"y")||0,a=s-e,c=o-i;return{left:e,top:i,width:a,height:c}},toObject:function(t){return i(this.callSuper("toObject",t),{points:this.points.concat()})},_toSVG:function(){for(var t=[],i=this.pathOffset.x,r=this.pathOffset.y,n=e.Object.NUM_FRACTION_DIGITS,o=0,a=this.points.length;a>o;o++)t.push(s(this.points[o].x-i,n),",",s(this.points[o].y-r,n)," ");return["<"+this.type+" ","COMMON_PARTS",'points="',t.join(""),'" />\n']},commonRender:function(t){var e,i=this.points.length,r=this.pathOffset.x,n=this.pathOffset.y;if(!i||isNaN(this.points[i-1].y))return!1;t.beginPath(),t.moveTo(this.points[0].x-r,this.points[0].y-n);for(var s=0;i>s;s++)e=this.points[s],t.lineTo(e.x-r,e.y-n);return!0},_render:function(t){this.commonRender(t)&&this._renderPaintInOrder(t)},complexity:function(){return this.get("points").length}}),e.Polyline.ATTRIBUTE_NAMES=e.SHARED_ATTRIBUTES.concat(),e.Polyline.fromElementGenerator=function(t){return function(r,n,s){if(!r)return n(null);s||(s={});var o=e.parsePointsAttribute(r.getAttribute("points")),a=e.parseAttributes(r,e[t].ATTRIBUTE_NAMES);a.fromSVG=!0,n(new e[t](o,i(a,s)))}},e.Polyline.fromElement=e.Polyline.fromElementGenerator("Polyline"),void(e.Polyline.fromObject=function(t,i){return e.Object._fromObject("Polyline",t,i,"points")}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.projectStrokeOnPoints;return e.Polygon?void e.warn("fabric.Polygon is already defined"):(e.Polygon=e.util.createClass(e.Polyline,{type:"polygon",_projectStrokeOnPoints:function(){return i(this.points,this)},_render:function(t){this.commonRender(t)&&(t.closePath(),this._renderPaintInOrder(t))}}),e.Polygon.ATTRIBUTE_NAMES=e.SHARED_ATTRIBUTES.concat(),e.Polygon.fromElement=e.Polyline.fromElementGenerator("Polygon"),void(e.Polygon.fromObject=function(t,i){e.Object._fromObject("Polygon",t,i,"points")}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.array.min,r=e.util.array.max,n=e.util.object.extend,s=e.util.object.clone,o=e.util.toFixed;return e.Path?void e.warn("fabric.Path is already defined"):(e.Path=e.util.createClass(e.Object,{type:"path",path:null,cacheProperties:e.Object.prototype.cacheProperties.concat("path","fillRule"),stateProperties:e.Object.prototype.stateProperties.concat("path"),initialize:function(t,e){e=s(e||{}),delete e.path,this.callSuper("initialize",e),this._setPath(t||[],e)},_setPath:function(t,i){this.path=e.util.makePathSimpler(Array.isArray(t)?t:e.util.parsePath(t)),e.Polyline.prototype._setPositionDimensions.call(this,i||{})},_renderPathCommands:function(t){var e,i=0,r=0,n=0,s=0,o=0,a=0,c=-this.pathOffset.x,h=-this.pathOffset.y;t.beginPath();for(var l=0,u=this.path.length;u>l;++l)switch(e=this.path[l],e[0]){case"L":n=e[1],s=e[2],t.lineTo(n+c,s+h);break;case"M":n=e[1],s=e[2],i=n,r=s,t.moveTo(n+c,s+h);break;case"C":n=e[5],s=e[6],o=e[3],a=e[4],t.bezierCurveTo(e[1]+c,e[2]+h,o+c,a+h,n+c,s+h);break;case"Q":t.quadraticCurveTo(e[1]+c,e[2]+h,e[3]+c,e[4]+h),n=e[3],s=e[4],o=e[1],a=e[2];break;case"z":case"Z":n=i,s=r,t.closePath()}},_render:function(t){this._renderPathCommands(t),this._renderPaintInOrder(t)},toString:function(){return"#"},toObject:function(t){return n(this.callSuper("toObject",t),{path:this.path.map(function(t){return t.slice()})})},toDatalessObject:function(t){var e=this.toObject(["sourcePath"].concat(t));return e.sourcePath&&delete e.path,e},_toSVG:function(){var t=e.util.joinPath(this.path);return["\n"]},_getOffsetTransform:function(){var t=e.Object.NUM_FRACTION_DIGITS;return" translate("+o(-this.pathOffset.x,t)+", "+o(-this.pathOffset.y,t)+")"},toClipPathSVG:function(t){var e=this._getOffsetTransform();return" "+this._createBaseClipPathSVGMarkup(this._toSVG(),{reviver:t,additionalTransform:e})},toSVG:function(t){var e=this._getOffsetTransform();return this._createBaseSVGMarkup(this._toSVG(),{reviver:t,additionalTransform:e})},complexity:function(){return this.path.length},_calcDimensions:function(){for(var t,n,s=[],o=[],a=0,c=0,h=0,l=0,u=0,f=this.path.length;f>u;++u){switch(t=this.path[u],t[0]){case"L":h=t[1],l=t[2],n=[];break;case"M":h=t[1],l=t[2],a=h,c=l,n=[];break;case"C":n=e.util.getBoundsOfCurve(h,l,t[1],t[2],t[3],t[4],t[5],t[6]),h=t[5],l=t[6];break;case"Q":n=e.util.getBoundsOfCurve(h,l,t[1],t[2],t[1],t[2],t[3],t[4]),h=t[3],l=t[4];break;case"z":case"Z":h=a,l=c}n.forEach(function(t){s.push(t.x),o.push(t.y)}),s.push(h),o.push(l)}var d=i(s)||0,g=i(o)||0,p=r(s)||0,v=r(o)||0,m=p-d,b=v-g;return{left:d,top:g,width:m,height:b}}}),e.Path.fromObject=function(t,i){if("string"==typeof t.sourcePath){var r=t.sourcePath;e.loadSVGFromURL(r,function(e){var r=e[0];r.setOptions(t),i&&i(r)})}else e.Object._fromObject("Path",t,i,"path")},e.Path.ATTRIBUTE_NAMES=e.SHARED_ATTRIBUTES.concat(["d"]),void(e.Path.fromElement=function(t,i,r){var s=e.parseAttributes(t,e.Path.ATTRIBUTE_NAMES);s.fromSVG=!0,i(new e.Path(s.d,n(s,r)))}))}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.array.min,r=e.util.array.max;e.Group||(e.Group=e.util.createClass(e.Object,e.Collection,{type:"group",strokeWidth:0,subTargetCheck:!1,cacheProperties:[],useSetOnGroup:!1,initialize:function(t,e,i){e=e||{},this._objects=[],i&&this.callSuper("initialize",e),this._objects=t||[];for(var r=this._objects.length;r--;)this._objects[r].group=this;if(i)this._updateObjectsACoords();else{var n=e&&e.centerPoint;void 0!==e.originX&&(this.originX=e.originX),void 0!==e.originY&&(this.originY=e.originY),n||this._calcBounds(),this._updateObjectsCoords(n),delete e.centerPoint,this.callSuper("initialize",e)}this.setCoords()},_updateObjectsACoords:function(){for(var t=!0,e=this._objects.length;e--;)this._objects[e].setCoords(t)},_updateObjectsCoords:function(t){for(var t=t||this.getCenterPoint(),e=this._objects.length;e--;)this._updateObjectCoords(this._objects[e],t)},_updateObjectCoords:function(t,e){var i=t.left,r=t.top,n=!0;t.set({left:i-e.x,top:r-e.y}),t.group=this,t.setCoords(n)},toString:function(){return"#"},addWithUpdate:function(t){var i=!!this.group;return this._restoreObjectsState(),e.util.resetObjectTransform(this),t&&(i&&e.util.removeTransformFromObject(t,this.group.calcTransformMatrix()),this._objects.push(t),t.group=this,t._set("canvas",this.canvas)),this._calcBounds(),this._updateObjectsCoords(),this.dirty=!0,i?this.group.addWithUpdate():this.setCoords(),this},removeWithUpdate:function(t){return this._restoreObjectsState(),e.util.resetObjectTransform(this),this.remove(t),this._calcBounds(),this._updateObjectsCoords(),this.setCoords(),this.dirty=!0,this},_onObjectAdded:function(t){this.dirty=!0,t.group=this,t._set("canvas",this.canvas)},_onObjectRemoved:function(t){this.dirty=!0,delete t.group},_set:function(t,i){var r=this._objects.length;if(this.useSetOnGroup)for(;r--;)this._objects[r].setOnGroup(t,i);if("canvas"===t)for(;r--;)this._objects[r]._set(t,i);e.Object.prototype._set.call(this,t,i)},toObject:function(t){var i=this.includeDefaultValues,r=this._objects.filter(function(t){return!t.excludeFromExport}).map(function(e){var r=e.includeDefaultValues;e.includeDefaultValues=i;var n=e.toObject(t);return e.includeDefaultValues=r,n}),n=e.Object.prototype.toObject.call(this,t);return n.objects=r,n},toDatalessObject:function(t){var i,r=this.sourcePath;if(r)i=r;else{var n=this.includeDefaultValues;i=this._objects.map(function(e){var i=e.includeDefaultValues;e.includeDefaultValues=n;var r=e.toDatalessObject(t);return e.includeDefaultValues=i,r})}var s=e.Object.prototype.toDatalessObject.call(this,t);return s.objects=i,s},render:function(t){this._transformDone=!0,this.callSuper("render",t),this._transformDone=!1},shouldCache:function(){var t=e.Object.prototype.shouldCache.call(this);if(t)for(var i=0,r=this._objects.length;r>i;i++)if(this._objects[i].willDrawShadow())return this.ownCaching=!1,!1;return t},willDrawShadow:function(){if(e.Object.prototype.willDrawShadow.call(this))return!0;for(var t=0,i=this._objects.length;i>t;t++)if(this._objects[t].willDrawShadow())return!0;return!1},isOnACache:function(){return this.ownCaching||this.group&&this.group.isOnACache()},drawObject:function(t){for(var e=0,i=this._objects.length;i>e;e++)this._objects[e].render(t);this._drawClipPath(t,this.clipPath)},isCacheDirty:function(t){if(this.callSuper("isCacheDirty",t))return!0;if(!this.statefullCache)return!1;for(var e=0,i=this._objects.length;i>e;e++)if(this._objects[e].isCacheDirty(!0)){if(this._cacheCanvas){var r=this.cacheWidth/this.zoomX,n=this.cacheHeight/this.zoomY;this._cacheContext.clearRect(-r/2,-n/2,r,n)}return!0}return!1},_restoreObjectsState:function(){var t=this.calcOwnMatrix();return this._objects.forEach(function(i){e.util.addTransformToObject(i,t),delete i.group,i.setCoords()}),this},destroy:function(){return this._objects.forEach(function(t){t.set("dirty",!0)}),this._restoreObjectsState()},dispose:function(){this.callSuper("dispose"),this.forEachObject(function(t){t.dispose&&t.dispose()}),this._objects=[]},toActiveSelection:function(){if(this.canvas){var t=this._objects,i=this.canvas;this._objects=[];var r=this.toObject();delete r.objects;var n=new e.ActiveSelection([]);return n.set(r),n.type="activeSelection",i.remove(this),t.forEach(function(t){t.group=n,t.dirty=!0,i.add(t)}),n.canvas=i,n._objects=t,i._activeObject=n,n.setCoords(),n}},ungroupOnCanvas:function(){return this._restoreObjectsState()},setObjectsCoords:function(){var t=!0;return this.forEachObject(function(e){e.setCoords(t)}),this},_calcBounds:function(t){for(var e,i,r,n,s=[],o=[],a=["tr","br","bl","tl"],c=0,h=this._objects.length,l=a.length;h>c;++c){for(e=this._objects[c],r=e.calcACoords(),n=0;l>n;n++)i=a[n],s.push(r[i].x),o.push(r[i].y);e.aCoords=r}this._getBounds(s,o,t)},_getBounds:function(t,n,s){var o=new e.Point(i(t),i(n)),a=new e.Point(r(t),r(n)),c=o.y||0,h=o.x||0,l=a.x-o.x||0,u=a.y-o.y||0;this.width=l,this.height=u,s||this.setPositionByOrigin({x:h,y:c},"left","top")},_toSVG:function(t){for(var e=["\n"],i=0,r=this._objects.length;r>i;i++)e.push(" ",this._objects[i].toSVG(t));return e.push("\n"),e},getSvgStyles:function(){var t="undefined"!=typeof this.opacity&&1!==this.opacity?"opacity: "+this.opacity+";":"",e=this.visible?"":" visibility: hidden;";return[t,this.getSvgFilter(),e].join("")},toClipPathSVG:function(t){for(var e=[],i=0,r=this._objects.length;r>i;i++)e.push(" ",this._objects[i].toClipPathSVG(t));return this._createBaseClipPathSVGMarkup(e,{reviver:t})}}),e.Group.fromObject=function(t,i){var r=t.objects,n=e.util.object.clone(t,!0);return delete n.objects,"string"==typeof r?void e.loadSVGFromURL(r,function(s){var o=e.util.groupSVGElements(s,t,r);o.set(n),i&&i(o)}):void e.util.enlivenObjects(r,function(r){var n=e.util.object.clone(t,!0);delete n.objects,e.util.enlivenObjectEnlivables(t,n,function(){i&&i(new e.Group(r,n,!0))})})})}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={});e.ActiveSelection||(e.ActiveSelection=e.util.createClass(e.Group,{type:"activeSelection",initialize:function(t,i){i=i||{},this._objects=t||[];for(var r=this._objects.length;r--;)this._objects[r].group=this;i.originX&&(this.originX=i.originX),i.originY&&(this.originY=i.originY),this._calcBounds(),this._updateObjectsCoords(),e.Object.prototype.initialize.call(this,i),this.setCoords()},toGroup:function(){var t=this._objects.concat();this._objects=[];var i=e.Object.prototype.toObject.call(this),r=new e.Group([]);if(delete i.type,r.set(i),t.forEach(function(t){t.canvas.remove(t),t.group=r}),r._objects=t,!this.canvas)return r;var n=this.canvas;return n.add(r),n._activeObject=r,r.setCoords(),r},onDeselect:function(){return this.destroy(),!1},toString:function(){return"#"},shouldCache:function(){return!1},isOnACache:function(){return!1},_renderControls:function(t,e,i){t.save(),t.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1,this.callSuper("_renderControls",t,e),i=i||{},"undefined"==typeof i.hasControls&&(i.hasControls=!1),i.forActiveSelection=!0;for(var r=0,n=this._objects.length;n>r;r++)this._objects[r]._renderControls(t,i);t.restore()}}),e.ActiveSelection.fromObject=function(t,i){e.util.enlivenObjects(t.objects,function(r){delete t.objects,i&&i(new e.ActiveSelection(r,t,!0))})})}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=fabric.util.object.extend;return t.fabric||(t.fabric={}),t.fabric.Image?void fabric.warn("fabric.Image is already defined."):(fabric.Image=fabric.util.createClass(fabric.Object,{type:"image",strokeWidth:0,srcFromAttribute:!1,_lastScaleX:1,_lastScaleY:1,_filterScalingX:1,_filterScalingY:1,minimumScaleTrigger:.5,stateProperties:fabric.Object.prototype.stateProperties.concat("cropX","cropY"),cacheProperties:fabric.Object.prototype.cacheProperties.concat("cropX","cropY"),cacheKey:"",cropX:0,cropY:0,imageSmoothing:!0,initialize:function(t,e){e||(e={}),this.filters=[],this.cacheKey="texture"+fabric.Object.__uid++,this.callSuper("initialize",e),this._initElement(t,e)},getElement:function(){return this._element||{}},setElement:function(t,e){return this.removeTexture(this.cacheKey),this.removeTexture(this.cacheKey+"_filtered"),this._element=t,this._originalElement=t,this._initConfig(e),0!==this.filters.length&&this.applyFilters(),this.resizeFilter&&this.applyResizeFilters(),this},removeTexture:function(t){var e=fabric.filterBackend;e&&e.evictCachesForKey&&e.evictCachesForKey(t)},dispose:function(){this.callSuper("dispose"),this.removeTexture(this.cacheKey),this.removeTexture(this.cacheKey+"_filtered"),this._cacheContext=void 0,["_originalElement","_element","_filteredEl","_cacheCanvas"].forEach(function(t){fabric.util.cleanUpJsdomNode(this[t]),this[t]=void 0}.bind(this))},getCrossOrigin:function(){return this._originalElement&&(this._originalElement.crossOrigin||null)},getOriginalSize:function(){var t=this.getElement();return{width:t.naturalWidth||t.width,height:t.naturalHeight||t.height}},_stroke:function(t){if(this.stroke&&0!==this.strokeWidth){var e=this.width/2,i=this.height/2;t.beginPath(),t.moveTo(-e,-i),t.lineTo(e,-i),t.lineTo(e,i),t.lineTo(-e,i),t.lineTo(-e,-i),t.closePath()}},toObject:function(t){var i=[];this.filters.forEach(function(t){t&&i.push(t.toObject())});var r=e(this.callSuper("toObject",["cropX","cropY"].concat(t)),{src:this.getSrc(),crossOrigin:this.getCrossOrigin(),filters:i});return this.resizeFilter&&(r.resizeFilter=this.resizeFilter.toObject()),r},hasCrop:function(){return this.cropX||this.cropY||this.width\n',' \n',"\n"),o=' clip-path="url(#imageCrop_'+c+')" '}if(this.imageSmoothing||(a='" image-rendering="optimizeSpeed'),i.push(" \n"),this.stroke||this.strokeDashArray){var h=this.fill;this.fill=null,t=[" \n'],this.fill=h}return e="fill"!==this.paintFirst?e.concat(t,i):e.concat(i,t)},getSrc:function(t){var e=t?this._element:this._originalElement;return e?e.toDataURL?e.toDataURL():this.srcFromAttribute?e.getAttribute("src"):e.src:this.src||""},setSrc:function(t,e,i){return fabric.util.loadImage(t,function(t,r){this.setElement(t,i),this._setWidthHeight(),e&&e(this,r)},this,i&&i.crossOrigin),this},toString:function(){return'#'},applyResizeFilters:function(){var t=this.resizeFilter,e=this.minimumScaleTrigger,i=this.getTotalObjectScaling(),r=i.scaleX,n=i.scaleY,s=this._filteredEl||this._originalElement;if(this.group&&this.set("dirty",!0),!t||r>e&&n>e)return this._element=s,this._filterScalingX=1,this._filterScalingY=1,this._lastScaleX=r,void(this._lastScaleY=n);fabric.filterBackend||(fabric.filterBackend=fabric.initFilterBackend());var o=fabric.util.createCanvasElement(),a=this._filteredEl?this.cacheKey+"_filtered":this.cacheKey,c=s.width,h=s.height;o.width=c,o.height=h,this._element=o,this._lastScaleX=t.scaleX=r,this._lastScaleY=t.scaleY=n,fabric.filterBackend.applyFilters([t],s,c,h,this._element,a),this._filterScalingX=o.width/this._originalElement.width,this._filterScalingY=o.height/this._originalElement.height},applyFilters:function(t){if(t=t||this.filters||[],t=t.filter(function(t){return t&&!t.isNeutralState()}),this.set("dirty",!0),this.removeTexture(this.cacheKey+"_filtered"),0===t.length)return this._element=this._originalElement,this._filteredEl=null,this._filterScalingX=1,this._filterScalingY=1,this;var e=this._originalElement,i=e.naturalWidth||e.width,r=e.naturalHeight||e.height;if(this._element===this._originalElement){var n=fabric.util.createCanvasElement();n.width=i,n.height=r,this._element=n,this._filteredEl=n}else this._element=this._filteredEl,this._filteredEl.getContext("2d").clearRect(0,0,i,r),this._lastScaleX=1,this._lastScaleY=1;return fabric.filterBackend||(fabric.filterBackend=fabric.initFilterBackend()),fabric.filterBackend.applyFilters(t,this._originalElement,i,r,this._element,this.cacheKey),(this._originalElement.width!==this._element.width||this._originalElement.height!==this._element.height)&&(this._filterScalingX=this._element.width/this._originalElement.width,this._filterScalingY=this._element.height/this._originalElement.height),this},_render:function(t){fabric.util.setImageSmoothing(t,this.imageSmoothing),this.isMoving!==!0&&this.resizeFilter&&this._needsResize()&&this.applyResizeFilters(),this._stroke(t),this._renderPaintInOrder(t)},drawCacheOnCanvas:function(t){fabric.util.setImageSmoothing(t,this.imageSmoothing),fabric.Object.prototype.drawCacheOnCanvas.call(this,t)},shouldCache:function(){return this.needsItsOwnCache()},_renderFill:function(t){var e=this._element;if(e){var i=this._filterScalingX,r=this._filterScalingY,n=this.width,s=this.height,o=Math.min,a=Math.max,c=a(this.cropX,0),h=a(this.cropY,0),l=e.naturalWidth||e.width,u=e.naturalHeight||e.height,f=c*i,d=h*r,g=o(n*i,l-f),p=o(s*r,u-d),v=-n/2,m=-s/2,b=o(n,l/i-c),y=o(s,u/r-h);e&&t.drawImage(e,f,d,g,p,v,m,b,y)}},_needsResize:function(){var t=this.getTotalObjectScaling();return t.scaleX!==this._lastScaleX||t.scaleY!==this._lastScaleY},_resetWidthHeight:function(){this.set(this.getOriginalSize())},_initElement:function(t,e){this.setElement(fabric.util.getById(t),e),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(t){t||(t={}),this.setOptions(t),this._setWidthHeight(t)},_initFilters:function(t,e){t&&t.length?fabric.util.enlivenObjects(t,function(t){e&&e(t)},"fabric.Image.filters"):e&&e()},_setWidthHeight:function(t){t||(t={});var e=this.getElement();this.width=t.width||e.naturalWidth||e.width||0,this.height=t.height||e.naturalHeight||e.height||0},parsePreserveAspectRatioAttribute:function(){var t,e=fabric.util.parsePreserveAspectRatioAttribute(this.preserveAspectRatio||""),i=this._element.width,r=this._element.height,n=1,s=1,o=0,a=0,c=0,h=0,l=this.width,u=this.height,f={width:l,height:u};return!e||"none"===e.alignX&&"none"===e.alignY?(n=l/i,s=u/r):("meet"===e.meetOrSlice&&(n=s=fabric.util.findScaleToFit(this._element,f),t=(l-i*n)/2,"Min"===e.alignX&&(o=-t),"Max"===e.alignX&&(o=t),t=(u-r*s)/2,"Min"===e.alignY&&(a=-t),"Max"===e.alignY&&(a=t)),"slice"===e.meetOrSlice&&(n=s=fabric.util.findScaleToCover(this._element,f),t=i-l/n,"Mid"===e.alignX&&(c=t/2),"Max"===e.alignX&&(c=t),t=r-u/s,"Mid"===e.alignY&&(h=t/2),"Max"===e.alignY&&(h=t),i=l/n,r=u/s)),{width:i,height:r,scaleX:n,scaleY:s,offsetLeft:o,offsetTop:a,cropX:c,cropY:h}}}),fabric.Image.CSS_CANVAS="canvas-img",fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc,fabric.Image.fromObject=function(t,e){var i=fabric.util.object.clone(t);fabric.util.loadImage(i.src,function(t,r){return r?void(e&&e(null,!0)):void fabric.Image.prototype._initFilters.call(i,i.filters,function(r){i.filters=r||[],fabric.Image.prototype._initFilters.call(i,[i.resizeFilter],function(r){i.resizeFilter=r[0],fabric.util.enlivenObjectEnlivables(i,i,function(){var r=new fabric.Image(t,i);e(r,!1)})})})},null,i.crossOrigin)},fabric.Image.fromURL=function(t,e,i){fabric.util.loadImage(t,function(t,r){e&&e(new fabric.Image(t,i),r)},null,i&&i.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height preserveAspectRatio xlink:href crossOrigin image-rendering".split(" ")),void(fabric.Image.fromElement=function(t,i,r){var n=fabric.parseAttributes(t,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(n["xlink:href"],i,e(r?fabric.util.object.clone(r):{},n))}))}("undefined"!=typeof exports?exports:this);fabric.util.object.extend(fabric.Object.prototype,{_getAngleValueForStraighten:function(){var t=this.angle%360;return t>0?90*Math.round((t-1)/90):90*Math.round(t/90)},straighten:function(){return this.rotate(this._getAngleValueForStraighten())},fxStraighten:function(t){t=t||{};var e=function(){},i=t.onComplete||e,r=t.onChange||e,n=this;return fabric.util.animate({target:this,startValue:this.get("angle"),endValue:this._getAngleValueForStraighten(),duration:this.FX_DURATION,onChange:function(t){n.rotate(t),r()},onComplete:function(){n.setCoords(),i()}})}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{straightenObject:function(t){return t.straighten(),this.requestRenderAll(),this},fxStraightenObject:function(t){return t.fxStraighten({onChange:this.requestRenderAllBound})}});function resizeCanvasIfNeeded(t){var e=t.targetCanvas,i=e.width,r=e.height,n=t.destinationWidth,o=t.destinationHeight;(i!==n||r!==o)&&(e.width=n,e.height=o)}function copyGLTo2DDrawImage(t,e){var i=t.canvas,r=e.targetCanvas,n=r.getContext("2d");n.translate(0,r.height),n.scale(1,-1);var o=i.height-r.height;n.drawImage(i,0,o,r.width,r.height,0,0,r.width,r.height)}function copyGLTo2DPutImageData(t,e){var i=e.targetCanvas,r=i.getContext("2d"),n=e.destinationWidth,o=e.destinationHeight,s=n*o*4,a=new Uint8Array(this.imageBuffer,0,s),c=new Uint8ClampedArray(this.imageBuffer,0,s);t.readPixels(0,0,n,o,t.RGBA,t.UNSIGNED_BYTE,a);var h=new ImageData(c,n,o);r.putImageData(h,0,0)}!function(){"use strict";function t(t,e){var i="precision "+e+" float;\nvoid main(){}",r=t.createShader(t.FRAGMENT_SHADER);return t.shaderSource(r,i),t.compileShader(r),t.getShaderParameter(r,t.COMPILE_STATUS)?!0:!1}function e(t){t&&t.tileSize&&(this.tileSize=t.tileSize),this.setupGLContext(this.tileSize,this.tileSize),this.captureGPUInfo()}fabric.isWebglSupported=function(e){if(fabric.isLikelyNode)return!1;e=e||fabric.WebglFilterBackend.prototype.tileSize;var i=document.createElement("canvas"),r=i.getContext("webgl")||i.getContext("experimental-webgl"),n=!1;if(r){fabric.maxTextureSize=r.getParameter(r.MAX_TEXTURE_SIZE),n=fabric.maxTextureSize>=e;for(var o=["highp","mediump","lowp"],s=0;3>s;s++)if(t(r,o[s])){fabric.webGlPrecision=o[s];break}}return this.isSupported=n,n},fabric.WebglFilterBackend=e,e.prototype={tileSize:2048,resources:{},setupGLContext:function(t,e){this.dispose(),this.createWebGLCanvas(t,e),this.aPosition=new Float32Array([0,0,0,1,1,0,1,1]),this.chooseFastestCopyGLTo2DMethod(t,e)},chooseFastestCopyGLTo2DMethod:function(t,e){var i,r="undefined"!=typeof window.performance;try{new ImageData(1,1),i=!0}catch(n){i=!1}var o="undefined"!=typeof ArrayBuffer,s="undefined"!=typeof Uint8ClampedArray;if(r&&i&&o&&s){var a=fabric.util.createCanvasElement(),c=new ArrayBuffer(t*e*4);if(fabric.forceGLPutImageData)return this.imageBuffer=c,void(this.copyGLTo2D=copyGLTo2DPutImageData);var h,l,u,f={imageBuffer:c,destinationWidth:t,destinationHeight:e,targetCanvas:a};a.width=t,a.height=e,h=window.performance.now(),copyGLTo2DDrawImage.call(f,this.gl,f),l=window.performance.now()-h,h=window.performance.now(),copyGLTo2DPutImageData.call(f,this.gl,f),u=window.performance.now()-h,l>u?(this.imageBuffer=c,this.copyGLTo2D=copyGLTo2DPutImageData):this.copyGLTo2D=copyGLTo2DDrawImage}},createWebGLCanvas:function(t,e){var i=fabric.util.createCanvasElement();i.width=t,i.height=e;var r={alpha:!0,premultipliedAlpha:!1,depth:!1,stencil:!1,antialias:!1},n=i.getContext("webgl",r);n||(n=i.getContext("experimental-webgl",r)),n&&(n.clearColor(0,0,0,0),this.canvas=i,this.gl=n)},applyFilters:function(t,e,i,r,n,o){var s,a=this.gl;o&&(s=this.getCachedTexture(o,e));var c={originalWidth:e.width||e.originalWidth,originalHeight:e.height||e.originalHeight,sourceWidth:i,sourceHeight:r,destinationWidth:i,destinationHeight:r,context:a,sourceTexture:this.createTexture(a,i,r,!s&&e),targetTexture:this.createTexture(a,i,r),originalTexture:s||this.createTexture(a,i,r,!s&&e),passes:t.length,webgl:!0,aPosition:this.aPosition,programCache:this.programCache,pass:0,filterBackend:this,targetCanvas:n},h=a.createFramebuffer();return a.bindFramebuffer(a.FRAMEBUFFER,h),t.forEach(function(t){t&&t.applyTo(c)}),resizeCanvasIfNeeded(c),this.copyGLTo2D(a,c),a.bindTexture(a.TEXTURE_2D,null),a.deleteTexture(c.sourceTexture),a.deleteTexture(c.targetTexture),a.deleteFramebuffer(h),n.getContext("2d").setTransform(1,0,0,1,0,0),c},dispose:function(){this.canvas&&(this.canvas=null,this.gl=null),this.clearWebGLCaches()},clearWebGLCaches:function(){this.programCache={},this.textureCache={}},createTexture:function(t,e,i,r){var n=t.createTexture();return t.bindTexture(t.TEXTURE_2D,n),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),r?t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,r):t.texImage2D(t.TEXTURE_2D,0,t.RGBA,e,i,0,t.RGBA,t.UNSIGNED_BYTE,null),n},getCachedTexture:function(t,e){if(this.textureCache[t])return this.textureCache[t];var i=this.createTexture(this.gl,e.width,e.height,e);return this.textureCache[t]=i,i},evictCachesForKey:function(t){this.textureCache[t]&&(this.gl.deleteTexture(this.textureCache[t]),delete this.textureCache[t])},copyGLTo2D:copyGLTo2DDrawImage,captureGPUInfo:function(){if(this.gpuInfo)return this.gpuInfo;var t=this.gl,e={renderer:"",vendor:""};if(!t)return e;var i=t.getExtension("WEBGL_debug_renderer_info");if(i){var r=t.getParameter(i.UNMASKED_RENDERER_WEBGL),n=t.getParameter(i.UNMASKED_VENDOR_WEBGL);r&&(e.renderer=r.toLowerCase()),n&&(e.vendor=n.toLowerCase())}return this.gpuInfo=e,e}}}();!function(){"use strict";function t(){}var e=function(){};fabric.Canvas2dFilterBackend=t,t.prototype={evictCachesForKey:e,dispose:e,clearWebGLCaches:e,resources:{},applyFilters:function(t,e,i,r,n){var o=n.getContext("2d");o.drawImage(e,0,0,i,r);var s=o.getImageData(0,0,i,r),a=o.getImageData(0,0,i,r),c={sourceWidth:i,sourceHeight:r,imageData:s,originalEl:e,originalImageData:a,canvasEl:n,ctx:o,filterBackend:this};return t.forEach(function(t){t.applyTo(c)}),(c.imageData.width!==i||c.imageData.height!==r)&&(n.width=c.imageData.width,n.height=c.imageData.height),o.putImageData(c.imageData,0,0),c}}}();fabric.Image=fabric.Image||{},fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",vertexSource:"attribute vec2 aPosition;\nvarying vec2 vTexCoord;\nvoid main() {\nvTexCoord = aPosition;\ngl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\n}",fragmentSource:"precision highp float;\nvarying vec2 vTexCoord;\nuniform sampler2D uTexture;\nvoid main() {\ngl_FragColor = texture2D(uTexture, vTexCoord);\n}",initialize:function(t){t&&this.setOptions(t)},setOptions:function(t){for(var e in t)this[e]=t[e]},createProgram:function(t,e,i){e=e||this.fragmentSource,i=i||this.vertexSource,"highp"!==fabric.webGlPrecision&&(e=e.replace(/precision highp float/g,"precision "+fabric.webGlPrecision+" float"));var r=t.createShader(t.VERTEX_SHADER);if(t.shaderSource(r,i),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS))throw new Error("Vertex shader compile error for "+this.type+": "+t.getShaderInfoLog(r));var n=t.createShader(t.FRAGMENT_SHADER);if(t.shaderSource(n,e),t.compileShader(n),!t.getShaderParameter(n,t.COMPILE_STATUS))throw new Error("Fragment shader compile error for "+this.type+": "+t.getShaderInfoLog(n));var o=t.createProgram();if(t.attachShader(o,r),t.attachShader(o,n),t.linkProgram(o),!t.getProgramParameter(o,t.LINK_STATUS))throw new Error('Shader link error for "${this.type}" '+t.getProgramInfoLog(o));var s=this.getAttributeLocations(t,o),a=this.getUniformLocations(t,o)||{};return a.uStepW=t.getUniformLocation(o,"uStepW"),a.uStepH=t.getUniformLocation(o,"uStepH"),{program:o,attributeLocations:s,uniformLocations:a}},getAttributeLocations:function(t,e){return{aPosition:t.getAttribLocation(e,"aPosition")}},getUniformLocations:function(){return{}},sendAttributeData:function(t,e,i){var r=e.aPosition,n=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,n),t.enableVertexAttribArray(r),t.vertexAttribPointer(r,2,t.FLOAT,!1,0,0),t.bufferData(t.ARRAY_BUFFER,i,t.STATIC_DRAW)},_setupFrameBuffer:function(t){var e,i,r=t.context;t.passes>1?(e=t.destinationWidth,i=t.destinationHeight,(t.sourceWidth!==e||t.sourceHeight!==i)&&(r.deleteTexture(t.targetTexture),t.targetTexture=t.filterBackend.createTexture(r,e,i)),r.framebufferTexture2D(r.FRAMEBUFFER,r.COLOR_ATTACHMENT0,r.TEXTURE_2D,t.targetTexture,0)):(r.bindFramebuffer(r.FRAMEBUFFER,null),r.finish())},_swapTextures:function(t){t.passes--,t.pass++;var e=t.targetTexture;t.targetTexture=t.sourceTexture,t.sourceTexture=e},isNeutralState:function(){var t=this.mainParameter,e=fabric.Image.filters[this.type].prototype;if(t){if(Array.isArray(e[t])){for(var i=e[t].length;i--;)if(this[t][i]!==e[t][i])return!1;return!0}return e[t]===this[t]}return!1},applyTo:function(t){t.webgl?(this._setupFrameBuffer(t),this.applyToWebGL(t),this._swapTextures(t)):this.applyTo2d(t)},retrieveShader:function(t){return t.programCache.hasOwnProperty(this.type)||(t.programCache[this.type]=this.createProgram(t.context)),t.programCache[this.type]},applyToWebGL:function(t){var e=t.context,i=this.retrieveShader(t);0===t.pass&&t.originalTexture?e.bindTexture(e.TEXTURE_2D,t.originalTexture):e.bindTexture(e.TEXTURE_2D,t.sourceTexture),e.useProgram(i.program),this.sendAttributeData(e,i.attributeLocations,t.aPosition),e.uniform1f(i.uniformLocations.uStepW,1/t.sourceWidth),e.uniform1f(i.uniformLocations.uStepH,1/t.sourceHeight),this.sendUniformData(e,i.uniformLocations),e.viewport(0,0,t.destinationWidth,t.destinationHeight),e.drawArrays(e.TRIANGLE_STRIP,0,4)},bindAdditionalTexture:function(t,e,i){t.activeTexture(i),t.bindTexture(t.TEXTURE_2D,e),t.activeTexture(t.TEXTURE0)},unbindAdditionalTexture:function(t,e){t.activeTexture(e),t.bindTexture(t.TEXTURE_2D,null),t.activeTexture(t.TEXTURE0)},getMainParameter:function(){return this[this.mainParameter]},setMainParameter:function(t){this[this.mainParameter]=t},sendUniformData:function(){},createHelpLayer:function(t){if(!t.helpLayer){var e=document.createElement("canvas");e.width=t.sourceWidth,e.height=t.sourceHeight,t.helpLayer=e}},toObject:function(){var t={type:this.type},e=this.mainParameter;return e&&(t[e]=this[e]),t},toJSON:function(){return this.toObject()}}),fabric.Image.filters.BaseFilter.fromObject=function(t,e){var i=new fabric.Image.filters[t.type](t);return e&&e(i),i};!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,r=e.util.createClass;i.ColorMatrix=r(i.BaseFilter,{type:"ColorMatrix",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nvarying vec2 vTexCoord;\nuniform mat4 uColorMatrix;\nuniform vec4 uConstants;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\ncolor *= uColorMatrix;\ncolor += uConstants;\ngl_FragColor = color;\n}",matrix:[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],mainParameter:"matrix",colorsOnly:!0,initialize:function(t){this.callSuper("initialize",t),this.matrix=this.matrix.slice(0)},applyTo2d:function(t){var e,i,r,n,o,s=t.imageData,a=s.data,c=a.length,l=this.matrix,h=this.colorsOnly;for(o=0;c>o;o+=4)e=a[o],i=a[o+1],r=a[o+2],h?(a[o]=e*l[0]+i*l[1]+r*l[2]+255*l[4],a[o+1]=e*l[5]+i*l[6]+r*l[7]+255*l[9],a[o+2]=e*l[10]+i*l[11]+r*l[12]+255*l[14]):(n=a[o+3],a[o]=e*l[0]+i*l[1]+r*l[2]+n*l[3]+255*l[4],a[o+1]=e*l[5]+i*l[6]+r*l[7]+n*l[8]+255*l[9],a[o+2]=e*l[10]+i*l[11]+r*l[12]+n*l[13]+255*l[14],a[o+3]=e*l[15]+i*l[16]+r*l[17]+n*l[18]+255*l[19])},getUniformLocations:function(t,e){return{uColorMatrix:t.getUniformLocation(e,"uColorMatrix"),uConstants:t.getUniformLocation(e,"uConstants")}},sendUniformData:function(t,e){var i=this.matrix,r=[i[0],i[1],i[2],i[3],i[5],i[6],i[7],i[8],i[10],i[11],i[12],i[13],i[15],i[16],i[17],i[18]],n=[i[4],i[9],i[14],i[19]];t.uniformMatrix4fv(e.uColorMatrix,!1,r),t.uniform4fv(e.uConstants,n)}}),e.Image.filters.ColorMatrix.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,r=e.util.createClass;i.Brightness=r(i.BaseFilter,{type:"Brightness",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uBrightness;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\ncolor.rgb += uBrightness;\ngl_FragColor = color;\n}",brightness:0,mainParameter:"brightness",applyTo2d:function(t){if(0!==this.brightness){var e,i=t.imageData,r=i.data,n=r.length,o=Math.round(255*this.brightness);for(e=0;n>e;e+=4)r[e]=r[e]+o,r[e+1]=r[e+1]+o,r[e+2]=r[e+2]+o}},getUniformLocations:function(t,e){return{uBrightness:t.getUniformLocation(e,"uBrightness")}},sendUniformData:function(t,e){t.uniform1f(e.uBrightness,this.brightness)}}),e.Image.filters.Brightness.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend,r=e.Image.filters,n=e.util.createClass;r.Convolute=n(r.BaseFilter,{type:"Convolute",opaque:!1,matrix:[0,0,0,0,1,0,0,0,0],fragmentSource:{Convolute_3_1:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[9];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 0);\nfor (float h = 0.0; h < 3.0; h+=1.0) {\nfor (float w = 0.0; w < 3.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 1), uStepH * (h - 1));\ncolor += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 3.0 + w)];\n}\n}\ngl_FragColor = color;\n}",Convolute_3_0:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[9];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 1);\nfor (float h = 0.0; h < 3.0; h+=1.0) {\nfor (float w = 0.0; w < 3.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 1.0), uStepH * (h - 1.0));\ncolor.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 3.0 + w)];\n}\n}\nfloat alpha = texture2D(uTexture, vTexCoord).a;\ngl_FragColor = color;\ngl_FragColor.a = alpha;\n}",Convolute_5_1:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[25];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 0);\nfor (float h = 0.0; h < 5.0; h+=1.0) {\nfor (float w = 0.0; w < 5.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\ncolor += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 5.0 + w)];\n}\n}\ngl_FragColor = color;\n}",Convolute_5_0:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[25];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 1);\nfor (float h = 0.0; h < 5.0; h+=1.0) {\nfor (float w = 0.0; w < 5.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\ncolor.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 5.0 + w)];\n}\n}\nfloat alpha = texture2D(uTexture, vTexCoord).a;\ngl_FragColor = color;\ngl_FragColor.a = alpha;\n}",Convolute_7_1:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[49];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 0);\nfor (float h = 0.0; h < 7.0; h+=1.0) {\nfor (float w = 0.0; w < 7.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\ncolor += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 7.0 + w)];\n}\n}\ngl_FragColor = color;\n}",Convolute_7_0:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[49];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 1);\nfor (float h = 0.0; h < 7.0; h+=1.0) {\nfor (float w = 0.0; w < 7.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\ncolor.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 7.0 + w)];\n}\n}\nfloat alpha = texture2D(uTexture, vTexCoord).a;\ngl_FragColor = color;\ngl_FragColor.a = alpha;\n}",Convolute_9_1:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[81];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 0);\nfor (float h = 0.0; h < 9.0; h+=1.0) {\nfor (float w = 0.0; w < 9.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\ncolor += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 9.0 + w)];\n}\n}\ngl_FragColor = color;\n}",Convolute_9_0:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uMatrix[81];\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = vec4(0, 0, 0, 1);\nfor (float h = 0.0; h < 9.0; h+=1.0) {\nfor (float w = 0.0; w < 9.0; w+=1.0) {\nvec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\ncolor.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 9.0 + w)];\n}\n}\nfloat alpha = texture2D(uTexture, vTexCoord).a;\ngl_FragColor = color;\ngl_FragColor.a = alpha;\n}"},retrieveShader:function(t){var e=Math.sqrt(this.matrix.length),i=this.type+"_"+e+"_"+(this.opaque?1:0),r=this.fragmentSource[i];return t.programCache.hasOwnProperty(i)||(t.programCache[i]=this.createProgram(t.context,r)),t.programCache[i]},applyTo2d:function(t){var e,i,r,n,o,s,a,c,l,h,u,f,d,g=t.imageData,p=g.data,m=this.matrix,v=Math.round(Math.sqrt(m.length)),y=Math.floor(v/2),b=g.width,x=g.height,_=t.ctx.createImageData(b,x),C=_.data,S=this.opaque?1:0;for(u=0;x>u;u++)for(h=0;b>h;h++){for(o=4*(u*b+h),e=0,i=0,r=0,n=0,d=0;v>d;d++)for(f=0;v>f;f++)a=u+d-y,s=h+f-y,0>a||a>=x||0>s||s>=b||(c=4*(a*b+s),l=m[d*v+f],e+=p[c]*l,i+=p[c+1]*l,r+=p[c+2]*l,S||(n+=p[c+3]*l));C[o]=e,C[o+1]=i,C[o+2]=r,C[o+3]=S?p[o+3]:n}t.imageData=_},getUniformLocations:function(t,e){return{uMatrix:t.getUniformLocation(e,"uMatrix"),uOpaque:t.getUniformLocation(e,"uOpaque"),uHalfSize:t.getUniformLocation(e,"uHalfSize"),uSize:t.getUniformLocation(e,"uSize")}},sendUniformData:function(t,e){t.uniform1fv(e.uMatrix,this.matrix)},toObject:function(){return i(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),e.Image.filters.Convolute.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,r=e.util.createClass;i.Grayscale=r(i.BaseFilter,{type:"Grayscale",fragmentSource:{average:"precision highp float;\nuniform sampler2D uTexture;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nfloat average = (color.r + color.b + color.g) / 3.0;\ngl_FragColor = vec4(average, average, average, color.a);\n}",lightness:"precision highp float;\nuniform sampler2D uTexture;\nuniform int uMode;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 col = texture2D(uTexture, vTexCoord);\nfloat average = (max(max(col.r, col.g),col.b) + min(min(col.r, col.g),col.b)) / 2.0;\ngl_FragColor = vec4(average, average, average, col.a);\n}",luminosity:"precision highp float;\nuniform sampler2D uTexture;\nuniform int uMode;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 col = texture2D(uTexture, vTexCoord);\nfloat average = 0.21 * col.r + 0.72 * col.g + 0.07 * col.b;\ngl_FragColor = vec4(average, average, average, col.a);\n}"},mode:"average",mainParameter:"mode",applyTo2d:function(t){var e,i,r=t.imageData,n=r.data,s=n.length,o=this.mode;for(e=0;s>e;e+=4)"average"===o?i=(n[e]+n[e+1]+n[e+2])/3:"lightness"===o?i=(Math.min(n[e],n[e+1],n[e+2])+Math.max(n[e],n[e+1],n[e+2]))/2:"luminosity"===o&&(i=.21*n[e]+.72*n[e+1]+.07*n[e+2]),n[e]=i,n[e+1]=i,n[e+2]=i},retrieveShader:function(t){var e=this.type+"_"+this.mode;if(!t.programCache.hasOwnProperty(e)){var i=this.fragmentSource[this.mode];t.programCache[e]=this.createProgram(t.context,i)}return t.programCache[e]},getUniformLocations:function(t,e){return{uMode:t.getUniformLocation(e,"uMode")}},sendUniformData:function(t,e){var i=1;t.uniform1i(e.uMode,i)},isNeutralState:function(){return!1}}),e.Image.filters.Grayscale.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,r=e.util.createClass;i.Invert=r(i.BaseFilter,{type:"Invert",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform int uInvert;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nif (uInvert == 1) {\ngl_FragColor = vec4(1.0 - color.r,1.0 -color.g,1.0 -color.b,color.a);\n} else {\ngl_FragColor = color;\n}\n}",invert:!0,mainParameter:"invert",applyTo2d:function(t){var e,i=t.imageData,r=i.data,n=r.length;for(e=0;n>e;e+=4)r[e]=255-r[e],r[e+1]=255-r[e+1],r[e+2]=255-r[e+2]},isNeutralState:function(){return!this.invert},getUniformLocations:function(t,e){return{uInvert:t.getUniformLocation(e,"uInvert")}},sendUniformData:function(t,e){t.uniform1i(e.uInvert,this.invert)}}),e.Image.filters.Invert.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend,r=e.Image.filters,n=e.util.createClass;r.Noise=n(r.BaseFilter,{type:"Noise",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uStepH;\nuniform float uNoise;\nuniform float uSeed;\nvarying vec2 vTexCoord;\nfloat rand(vec2 co, float seed, float vScale) {\nreturn fract(sin(dot(co.xy * vScale ,vec2(12.9898 , 78.233))) * 43758.5453 * (seed + 0.01) / 2.0);\n}\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\ncolor.rgb += (0.5 - rand(vTexCoord, uSeed, 0.1 / uStepH)) * uNoise;\ngl_FragColor = color;\n}",mainParameter:"noise",noise:0,applyTo2d:function(t){if(0!==this.noise){var e,i,r=t.imageData,n=r.data,s=n.length,o=this.noise;for(e=0,s=n.length;s>e;e+=4)i=(.5-Math.random())*o,n[e]+=i,n[e+1]+=i,n[e+2]+=i}},getUniformLocations:function(t,e){return{uNoise:t.getUniformLocation(e,"uNoise"),uSeed:t.getUniformLocation(e,"uSeed")}},sendUniformData:function(t,e){t.uniform1f(e.uNoise,this.noise/255),t.uniform1f(e.uSeed,Math.random())},toObject:function(){return i(this.callSuper("toObject"),{noise:this.noise})}}),e.Image.filters.Noise.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,r=e.util.createClass;i.Pixelate=r(i.BaseFilter,{type:"Pixelate",blocksize:4,mainParameter:"blocksize",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uBlocksize;\nuniform float uStepW;\nuniform float uStepH;\nvarying vec2 vTexCoord;\nvoid main() {\nfloat blockW = uBlocksize * uStepW;\nfloat blockH = uBlocksize * uStepW;\nint posX = int(vTexCoord.x / blockW);\nint posY = int(vTexCoord.y / blockH);\nfloat fposX = float(posX);\nfloat fposY = float(posY);\nvec2 squareCoords = vec2(fposX * blockW, fposY * blockH);\nvec4 color = texture2D(uTexture, squareCoords);\ngl_FragColor = color;\n}",applyTo2d:function(t){var e,i,r,n,s,o,a,l,c,h,u,f=t.imageData,d=f.data,g=f.height,p=f.width;for(i=0;g>i;i+=this.blocksize)for(r=0;p>r;r+=this.blocksize)for(e=4*i*p+4*r,n=d[e],s=d[e+1],o=d[e+2],a=d[e+3],h=Math.min(i+this.blocksize,g),u=Math.min(r+this.blocksize,p),l=i;h>l;l++)for(c=r;u>c;c++)e=4*l*p+4*c,d[e]=n,d[e+1]=s,d[e+2]=o,d[e+3]=a},isNeutralState:function(){return 1===this.blocksize},getUniformLocations:function(t,e){return{uBlocksize:t.getUniformLocation(e,"uBlocksize"),uStepW:t.getUniformLocation(e,"uStepW"),uStepH:t.getUniformLocation(e,"uStepH")}},sendUniformData:function(t,e){t.uniform1f(e.uBlocksize,this.blocksize)}}),e.Image.filters.Pixelate.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.extend,r=e.Image.filters,n=e.util.createClass;r.RemoveColor=n(r.BaseFilter,{type:"RemoveColor",color:"#FFFFFF",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform vec4 uLow;\nuniform vec4 uHigh;\nvarying vec2 vTexCoord;\nvoid main() {\ngl_FragColor = texture2D(uTexture, vTexCoord);\nif(all(greaterThan(gl_FragColor.rgb,uLow.rgb)) && all(greaterThan(uHigh.rgb,gl_FragColor.rgb))) {\ngl_FragColor.a = 0.0;\n}\n}",distance:.02,useAlpha:!1,applyTo2d:function(t){var i,r,n,s,o=t.imageData,a=o.data,l=255*this.distance,c=new e.Color(this.color).getSource(),h=[c[0]-l,c[1]-l,c[2]-l],u=[c[0]+l,c[1]+l,c[2]+l];for(i=0;ih[0]&&n>h[1]&&s>h[2]&&r 0.0) {\n"+this.fragmentSource[t]+"}\n}"},retrieveShader:function(t){var e,i=this.type+"_"+this.mode;return t.programCache.hasOwnProperty(i)||(e=this.buildSource(this.mode),t.programCache[i]=this.createProgram(t.context,e)),t.programCache[i]},applyTo2d:function(t){var i,r,n,s,o,a,l,c=t.imageData,h=c.data,u=h.length,f=1-this.alpha;l=new e.Color(this.color).getSource(),i=l[0]*this.alpha,r=l[1]*this.alpha,n=l[2]*this.alpha;for(var d=0;u>d;d+=4)switch(s=h[d],o=h[d+1],a=h[d+2],this.mode){case"multiply":h[d]=s*i/255,h[d+1]=o*r/255,h[d+2]=a*n/255;break;case"screen":h[d]=255-(255-s)*(255-i)/255,h[d+1]=255-(255-o)*(255-r)/255,h[d+2]=255-(255-a)*(255-n)/255;break;case"add":h[d]=s+i,h[d+1]=o+r,h[d+2]=a+n;break;case"diff":case"difference":h[d]=Math.abs(s-i),h[d+1]=Math.abs(o-r),h[d+2]=Math.abs(a-n);break;case"subtract":h[d]=s-i,h[d+1]=o-r,h[d+2]=a-n;break;case"darken":h[d]=Math.min(s,i),h[d+1]=Math.min(o,r),h[d+2]=Math.min(a,n);break;case"lighten":h[d]=Math.max(s,i),h[d+1]=Math.max(o,r),h[d+2]=Math.max(a,n);break;case"overlay":h[d]=128>i?2*s*i/255:255-2*(255-s)*(255-i)/255,h[d+1]=128>r?2*o*r/255:255-2*(255-o)*(255-r)/255,h[d+2]=128>n?2*a*n/255:255-2*(255-a)*(255-n)/255;break;case"exclusion":h[d]=i+s-2*i*s/255,h[d+1]=r+o-2*r*o/255,h[d+2]=n+a-2*n*a/255;break;case"tint":h[d]=i+s*f,h[d+1]=r+o*f,h[d+2]=n+a*f}},getUniformLocations:function(t,e){return{uColor:t.getUniformLocation(e,"uColor")}},sendUniformData:function(t,i){var r=new e.Color(this.color).getSource();r[0]=this.alpha*r[0]/255,r[1]=this.alpha*r[1]/255,r[2]=this.alpha*r[2]/255,r[3]=this.alpha,t.uniform4fv(i.uColor,r)},toObject:function(){return{type:this.type,color:this.color,mode:this.mode,alpha:this.alpha}}}),e.Image.filters.BlendColor.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric,i=e.Image.filters,n=e.util.createClass;i.BlendImage=n(i.BaseFilter,{type:"BlendImage",image:null,mode:"multiply",alpha:1,vertexSource:"attribute vec2 aPosition;\nvarying vec2 vTexCoord;\nvarying vec2 vTexCoord2;\nuniform mat3 uTransformMatrix;\nvoid main() {\nvTexCoord = aPosition;\nvTexCoord2 = (uTransformMatrix * vec3(aPosition, 1.0)).xy;\ngl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\n}",fragmentSource:{multiply:"precision highp float;\nuniform sampler2D uTexture;\nuniform sampler2D uImage;\nuniform vec4 uColor;\nvarying vec2 vTexCoord;\nvarying vec2 vTexCoord2;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nvec4 color2 = texture2D(uImage, vTexCoord2);\ncolor.rgba *= color2.rgba;\ngl_FragColor = color;\n}",mask:"precision highp float;\nuniform sampler2D uTexture;\nuniform sampler2D uImage;\nuniform vec4 uColor;\nvarying vec2 vTexCoord;\nvarying vec2 vTexCoord2;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nvec4 color2 = texture2D(uImage, vTexCoord2);\ncolor.a = color2.a;\ngl_FragColor = color;\n}"},retrieveShader:function(t){var e=this.type+"_"+this.mode,i=this.fragmentSource[this.mode];return t.programCache.hasOwnProperty(e)||(t.programCache[e]=this.createProgram(t.context,i)),t.programCache[e]},applyToWebGL:function(t){var e=t.context,i=this.createTexture(t.filterBackend,this.image);this.bindAdditionalTexture(e,i,e.TEXTURE1),this.callSuper("applyToWebGL",t),this.unbindAdditionalTexture(e,e.TEXTURE1)},createTexture:function(t,e){return t.getCachedTexture(e.cacheKey,e._element)},calculateMatrix:function(){var t=this.image,e=t._element.width,i=t._element.height;return[1/t.scaleX,0,0,0,1/t.scaleY,0,-t.left/e,-t.top/i,1]},applyTo2d:function(t){var i,n,r,s,a,o,c,l,h,u,f,d=t.imageData,g=t.filterBackend.resources,p=d.data,m=p.length,y=d.width,v=d.height,x=this.image;g.blendImage||(g.blendImage=e.util.createCanvasElement()),h=g.blendImage,u=h.getContext("2d"),h.width!==y||h.height!==v?(h.width=y,h.height=v):u.clearRect(0,0,y,v),u.setTransform(x.scaleX,0,0,x.scaleY,x.left,x.top),u.drawImage(x._element,0,0,y,v),f=u.getImageData(0,0,y,v).data;for(var b=0;m>b;b+=4)switch(a=p[b],o=p[b+1],c=p[b+2],l=p[b+3],i=f[b],n=f[b+1],r=f[b+2],s=f[b+3],this.mode){case"multiply":p[b]=a*i/255,p[b+1]=o*n/255,p[b+2]=c*r/255,p[b+3]=l*s/255;break;case"mask":p[b+3]=s}},getUniformLocations:function(t,e){return{uTransformMatrix:t.getUniformLocation(e,"uTransformMatrix"),uImage:t.getUniformLocation(e,"uImage")}},sendUniformData:function(t,e){var i=this.calculateMatrix();t.uniform1i(e.uImage,1),t.uniformMatrix3fv(e.uTransformMatrix,!1,i)},toObject:function(){return{type:this.type,image:this.image&&this.image.toObject(),mode:this.mode,alpha:this.alpha}}}),e.Image.filters.BlendImage.fromObject=function(t,i){e.Image.fromObject(t.image,function(n){var r=e.util.object.clone(t);r.image=n,i(new e.Image.filters.BlendImage(r))})}}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=Math.pow,n=Math.floor,r=Math.sqrt,s=Math.abs,a=Math.round,o=Math.sin,c=Math.ceil,l=e.Image.filters,h=e.util.createClass;l.Resize=h(l.BaseFilter,{type:"Resize",resizeType:"hermite",scaleX:1,scaleY:1,lanczosLobes:3,getUniformLocations:function(t,e){return{uDelta:t.getUniformLocation(e,"uDelta"),uTaps:t.getUniformLocation(e,"uTaps")}},sendUniformData:function(t,e){t.uniform2fv(e.uDelta,this.horizontal?[1/this.width,0]:[0,1/this.height]),t.uniform1fv(e.uTaps,this.taps)},retrieveShader:function(t){var e=this.getFilterWindow(),i=this.type+"_"+e;if(!t.programCache.hasOwnProperty(i)){var n=this.generateShader(e);t.programCache[i]=this.createProgram(t.context,n)}return t.programCache[i]},getFilterWindow:function(){var t=this.tempScale;return Math.ceil(this.lanczosLobes/t)},getTaps:function(){for(var t=this.lanczosCreate(this.lanczosLobes),e=this.tempScale,i=this.getFilterWindow(),n=new Array(i),r=1;i>=r;r++)n[r-1]=t(r*e);return n},generateShader:function(t){for(var t,e=new Array(t),i=this.fragmentSourceTOP,n=1;t>=n;n++)e[n-1]=n+".0 * uDelta";return i+="uniform float uTaps["+t+"];\n",i+="void main() {\n",i+=" vec4 color = texture2D(uTexture, vTexCoord);\n",i+=" float sum = 1.0;\n",e.forEach(function(t,e){i+=" color += texture2D(uTexture, vTexCoord + "+t+") * uTaps["+e+"];\n",i+=" color += texture2D(uTexture, vTexCoord - "+t+") * uTaps["+e+"];\n",i+=" sum += 2.0 * uTaps["+e+"];\n"}),i+=" gl_FragColor = color / sum;\n",i+="}"},fragmentSourceTOP:"precision highp float;\nuniform sampler2D uTexture;\nuniform vec2 uDelta;\nvarying vec2 vTexCoord;\n",applyTo:function(t){t.webgl?(t.passes++,this.width=t.sourceWidth,this.horizontal=!0,this.dW=Math.round(this.width*this.scaleX),this.dH=t.sourceHeight,this.tempScale=this.dW/this.width,this.taps=this.getTaps(),t.destinationWidth=this.dW,this._setupFrameBuffer(t),this.applyToWebGL(t),this._swapTextures(t),t.sourceWidth=t.destinationWidth,this.height=t.sourceHeight,this.horizontal=!1,this.dH=Math.round(this.height*this.scaleY),this.tempScale=this.dH/this.height,this.taps=this.getTaps(),t.destinationHeight=this.dH,this._setupFrameBuffer(t),this.applyToWebGL(t),this._swapTextures(t),t.sourceHeight=t.destinationHeight):this.applyTo2d(t)},isNeutralState:function(){return 1===this.scaleX&&1===this.scaleY},lanczosCreate:function(t){return function(e){if(e>=t||-t>=e)return 0;if(1.1920929e-7>e&&e>-1.1920929e-7)return 1;e*=Math.PI;var i=e/t;return o(e)/e*o(i)/i}},applyTo2d:function(t){var e=t.imageData,i=this.scaleX,n=this.scaleY;this.rcpScaleX=1/i,this.rcpScaleY=1/n;var r,s=e.width,o=e.height,c=a(s*i),l=a(o*n);"sliceHack"===this.resizeType?r=this.sliceByTwo(t,s,o,c,l):"hermite"===this.resizeType?r=this.hermiteFastResize(t,s,o,c,l):"bilinear"===this.resizeType?r=this.bilinearFiltering(t,s,o,c,l):"lanczos"===this.resizeType&&(r=this.lanczosResize(t,s,o,c,l)),t.imageData=r},sliceByTwo:function(t,i,r,s,a){var o,c,l=t.imageData,h=.5,u=!1,f=!1,d=i*h,g=r*h,p=e.filterBackend.resources,m=0,y=0,v=i,x=0;for(p.sliceByTwo||(p.sliceByTwo=document.createElement("canvas")),o=p.sliceByTwo,(o.width<1.5*i||o.heightc;c++){for(S.y=(c+.5)*m,C.y=n(S.y),k=0,O=0,A=0,E=0,D=0,w=C.x-x;w<=C.x+x;w++)if(!(0>w||w>=e)){M=n(1e3*s(w-S.x)),_[M]||(_[M]={});for(var P=C.y-b;P<=C.y+b;P++)0>P||P>=a||(L=n(1e3*s(P-S.y)),_[M][L]||(_[M][L]=g(r(i(M*y,2)+i(L*v,2))/1e3)),T=_[M][L],T>0&&(F=4*(P*e+w),k+=T,O+=T*u[F],A+=T*u[F+1],E+=T*u[F+2],D+=T*u[F+3]))}F=4*(c*o+t),d[F]=O/k,d[F+1]=A/k,d[F+2]=E/k,d[F+3]=D/k}return++tf;f++)for(d=0;r>d;d++)for(h=n(b*d),u=n(_*f),g=b*d-h,p=_*f-u,v=4*(u*e+h),m=0;4>m;m++)a=w[v+m],o=w[v+4+m],c=w[v+S+m],l=w[v+S+4+m],y=a*(1-g)*(1-p)+o*g*(1-p)+c*p*(1-g)+l*g*p,F[x++]=y;return T},hermiteFastResize:function(t,e,i,a,o){for(var l=this.rcpScaleX,h=this.rcpScaleY,u=c(l/2),f=c(h/2),d=t.imageData,g=d.data,p=t.ctx.createImageData(a,o),m=p.data,y=0;o>y;y++)for(var v=0;a>v;v++){for(var x=4*(v+y*a),b=0,_=0,S=0,C=0,w=0,T=0,F=0,k=(y+.5)*h,O=n(y*h);(y+1)*h>O;O++)for(var A=s(k-(O+.5))/f,E=(v+.5)*l,D=A*A,M=n(v*l);(v+1)*l>M;M++){var L=s(E-(M+.5))/u,P=r(D+L*L);P>1&&-1>P||(b=2*P*P*P-3*P*P+1,b>0&&(L=4*(M+O*e),F+=b*g[L+3],S+=b,g[L+3]<255&&(b=b*g[L+3]/250),C+=b*g[L],w+=b*g[L+1],T+=b*g[L+2],_+=b))}m[x]=C/_,m[x+1]=w/_,m[x+2]=T/_,m[x+3]=F/S}return p},toObject:function(){return{type:this.type,scaleX:this.scaleX,scaleY:this.scaleY,resizeType:this.resizeType,lanczosLobes:this.lanczosLobes}}}),e.Image.filters.Resize.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.Contrast=n(i.BaseFilter,{type:"Contrast",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uContrast;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nfloat contrastF = 1.015 * (uContrast + 1.0) / (1.0 * (1.015 - uContrast));\ncolor.rgb = contrastF * (color.rgb - 0.5) + 0.5;\ngl_FragColor = color;\n}",contrast:0,mainParameter:"contrast",applyTo2d:function(t){if(0!==this.contrast){var e,i,n=t.imageData,r=n.data,i=r.length,s=Math.floor(255*this.contrast),a=259*(s+255)/(255*(259-s));for(e=0;i>e;e+=4)r[e]=a*(r[e]-128)+128,r[e+1]=a*(r[e+1]-128)+128,r[e+2]=a*(r[e+2]-128)+128}},getUniformLocations:function(t,e){return{uContrast:t.getUniformLocation(e,"uContrast")}},sendUniformData:function(t,e){t.uniform1f(e.uContrast,this.contrast)}}),e.Image.filters.Contrast.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.Saturation=n(i.BaseFilter,{type:"Saturation",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform float uSaturation;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nfloat rgMax = max(color.r, color.g);\nfloat rgbMax = max(rgMax, color.b);\ncolor.r += rgbMax != color.r ? (rgbMax - color.r) * uSaturation : 0.00;\ncolor.g += rgbMax != color.g ? (rgbMax - color.g) * uSaturation : 0.00;\ncolor.b += rgbMax != color.b ? (rgbMax - color.b) * uSaturation : 0.00;\ngl_FragColor = color;\n}",saturation:0,mainParameter:"saturation",applyTo2d:function(t){if(0!==this.saturation){var e,i,n=t.imageData,r=n.data,s=r.length,a=-this.saturation;for(e=0;s>e;e+=4)i=Math.max(r[e],r[e+1],r[e+2]),r[e]+=i!==r[e]?(i-r[e])*a:0,r[e+1]+=i!==r[e+1]?(i-r[e+1])*a:0,r[e+2]+=i!==r[e+2]?(i-r[e+2])*a:0}},getUniformLocations:function(t,e){return{uSaturation:t.getUniformLocation(e,"uSaturation")}},sendUniformData:function(t,e){t.uniform1f(e.uSaturation,-this.saturation)}}),e.Image.filters.Saturation.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.Blur=n(i.BaseFilter,{type:"Blur",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform vec2 uDelta;\nvarying vec2 vTexCoord;\nconst float nSamples = 15.0;\nvec3 v3offset = vec3(12.9898, 78.233, 151.7182);\nfloat random(vec3 scale) {\nreturn fract(sin(dot(gl_FragCoord.xyz, scale)) * 43758.5453);\n}\nvoid main() {\nvec4 color = vec4(0.0);\nfloat total = 0.0;\nfloat offset = random(v3offset);\nfor (float t = -nSamples; t <= nSamples; t++) {\nfloat percent = (t + offset - 0.5) / nSamples;\nfloat weight = 1.0 - abs(percent);\ncolor += texture2D(uTexture, vTexCoord + uDelta * percent) * weight;\ntotal += weight;\n}\ngl_FragColor = color / total;\n}",blur:0,mainParameter:"blur",applyTo:function(t){t.webgl?(this.aspectRatio=t.sourceWidth/t.sourceHeight,t.passes++,this._setupFrameBuffer(t),this.horizontal=!0,this.applyToWebGL(t),this._swapTextures(t),this._setupFrameBuffer(t),this.horizontal=!1,this.applyToWebGL(t),this._swapTextures(t)):this.applyTo2d(t)},applyTo2d:function(t){t.imageData=this.simpleBlur(t)},simpleBlur:function(t){var i,n,r=t.filterBackend.resources,s=t.imageData.width,a=t.imageData.height;r.blurLayer1||(r.blurLayer1=e.util.createCanvasElement(),r.blurLayer2=e.util.createCanvasElement()),i=r.blurLayer1,n=r.blurLayer2,(i.width!==s||i.height!==a)&&(n.width=i.width=s,n.height=i.height=a);var o,c,l,h,f=i.getContext("2d"),u=n.getContext("2d"),d=15,p=.06*this.blur*.5;for(f.putImageData(t.imageData,0,0),u.clearRect(0,0,s,a),h=-d;d>=h;h++)o=(Math.random()-.5)/4,c=h/d,l=p*c*s+o,u.globalAlpha=1-Math.abs(c),u.drawImage(i,l,o),f.drawImage(n,0,0),u.globalAlpha=1,u.clearRect(0,0,n.width,n.height);for(h=-d;d>=h;h++)o=(Math.random()-.5)/4,c=h/d,l=p*c*a+o,u.globalAlpha=1-Math.abs(c),u.drawImage(i,o,l),f.drawImage(n,0,0),u.globalAlpha=1,u.clearRect(0,0,n.width,n.height);t.ctx.drawImage(i,0,0);var g=t.ctx.getImageData(0,0,i.width,i.height);return f.globalAlpha=1,f.clearRect(0,0,i.width,i.height),g},getUniformLocations:function(t,e){return{delta:t.getUniformLocation(e,"uDelta")}},sendUniformData:function(t,e){var i=this.chooseRightDelta();t.uniform2fv(e.delta,i)},chooseRightDelta:function(){var t,e=1,i=[0,0];return this.horizontal?this.aspectRatio>1&&(e=1/this.aspectRatio):this.aspectRatio<1&&(e=this.aspectRatio),t=e*this.blur*.12,this.horizontal?i[0]=t:i[1]=t,i}}),i.Blur.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.Gamma=n(i.BaseFilter,{type:"Gamma",fragmentSource:"precision highp float;\nuniform sampler2D uTexture;\nuniform vec3 uGamma;\nvarying vec2 vTexCoord;\nvoid main() {\nvec4 color = texture2D(uTexture, vTexCoord);\nvec3 correction = (1.0 / uGamma);\ncolor.r = pow(color.r, correction.r);\ncolor.g = pow(color.g, correction.g);\ncolor.b = pow(color.b, correction.b);\ngl_FragColor = color;\ngl_FragColor.rgb *= color.a;\n}",gamma:[1,1,1],mainParameter:"gamma",initialize:function(t){this.gamma=[1,1,1],i.BaseFilter.prototype.initialize.call(this,t)},applyTo2d:function(t){var e,i=t.imageData,n=i.data,r=this.gamma,s=n.length,a=1/r[0],o=1/r[1],c=1/r[2];for(this.rVals||(this.rVals=new Uint8Array(256),this.gVals=new Uint8Array(256),this.bVals=new Uint8Array(256)),e=0,s=256;s>e;e++)this.rVals[e]=255*Math.pow(e/255,a),this.gVals[e]=255*Math.pow(e/255,o),this.bVals[e]=255*Math.pow(e/255,c);for(e=0,s=n.length;s>e;e+=4)n[e]=this.rVals[n[e]],n[e+1]=this.gVals[n[e+1]],n[e+2]=this.bVals[n[e+2]]},getUniformLocations:function(t,e){return{uGamma:t.getUniformLocation(e,"uGamma")}},sendUniformData:function(t,e){t.uniform3fv(e.uGamma,this.gamma)}}),e.Image.filters.Gamma.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.Composed=n(i.BaseFilter,{type:"Composed",subFilters:[],initialize:function(t){this.callSuper("initialize",t),this.subFilters=this.subFilters.slice(0)},applyTo:function(t){t.passes+=this.subFilters.length-1,this.subFilters.forEach(function(e){e.applyTo(t)})},toObject:function(){return e.util.object.extend(this.callSuper("toObject"),{subFilters:this.subFilters.map(function(t){return t.toObject()})})},isNeutralState:function(){return!this.subFilters.some(function(t){return!t.isNeutralState()})}}),e.Image.filters.Composed.fromObject=function(t,i){var n=t.subFilters||[],r=n.map(function(t){return new e.Image.filters[t.type](t)}),s=new e.Image.filters.Composed({subFilters:r});return i&&i(s),s}}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.Image.filters,n=e.util.createClass;i.HueRotation=n(i.ColorMatrix,{type:"HueRotation",rotation:0,mainParameter:"rotation",calculateMatrix:function(){var t=this.rotation*Math.PI,i=e.util.cos(t),n=e.util.sin(t),r=1/3,s=Math.sqrt(r)*n,o=1-i;this.matrix=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0],this.matrix[0]=i+o/3,this.matrix[1]=r*o-s,this.matrix[2]=r*o+s,this.matrix[5]=r*o+s,this.matrix[6]=i+r*o,this.matrix[7]=r*o-s,this.matrix[10]=r*o-s,this.matrix[11]=r*o+s,this.matrix[12]=i+r*o},isNeutralState:function(t){return this.calculateMatrix(),i.BaseFilter.prototype.isNeutralState.call(this,t)},applyTo:function(t){this.calculateMatrix(),i.BaseFilter.prototype.applyTo.call(this,t)}}),e.Image.filters.HueRotation.fromObject=e.Image.filters.BaseFilter.fromObject}("undefined"!=typeof exports?exports:this);!function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.clone;if(e.Text)return void e.warn("fabric.Text is already defined");var n="fontFamily fontWeight fontSize text underline overline linethrough textAlign fontStyle lineHeight textBackgroundColor charSpacing styles direction path pathStartOffset pathSide pathAlign".split(" ");e.Text=e.util.createClass(e.Object,{_dimensionAffectingProps:["fontSize","fontWeight","fontFamily","fontStyle","lineHeight","text","charSpacing","textAlign","styles","path","pathStartOffset","pathSide","pathAlign"],_reNewline:/\r?\n/,_reSpacesAndTabs:/[ \t\r]/g,_reSpaceAndTab:/[ \t\r]/,_reWords:/\S+/g,type:"text",fontSize:40,fontWeight:"normal",fontFamily:"Times New Roman",underline:!1,overline:!1,linethrough:!1,textAlign:"left",fontStyle:"normal",lineHeight:1.16,superscript:{size:.6,baseline:-.35},subscript:{size:.6,baseline:.11},textBackgroundColor:"",stateProperties:e.Object.prototype.stateProperties.concat(n),cacheProperties:e.Object.prototype.cacheProperties.concat(n),stroke:null,shadow:null,path:null,pathStartOffset:0,pathSide:"left",pathAlign:"baseline",_fontSizeFraction:.222,offsets:{underline:.1,linethrough:-.315,overline:-.88},_fontSizeMult:1.13,charSpacing:0,styles:null,_measuringContext:null,deltaY:0,direction:"ltr",_styleProperties:["stroke","strokeWidth","fill","fontFamily","fontSize","fontWeight","fontStyle","underline","overline","linethrough","deltaY","textBackgroundColor"],__charBounds:[],CACHE_FONT_SIZE:400,MIN_TEXT_WIDTH:2,initialize:function(t,e){this.styles=e?e.styles||{}:{},this.text=t,this.__skipDimension=!0,this.callSuper("initialize",e),this.path&&this.setPathInfo(),this.__skipDimension=!1,this.initDimensions(),this.setCoords(),this.setupState({propertySet:"_dimensionAffectingProps"})},setPathInfo:function(){var t=this.path;t&&(t.segmentsInfo=e.util.getPathSegmentsInfo(t.path))},getMeasuringContext:function(){return e._measuringContext||(e._measuringContext=this.canvas&&this.canvas.contextCache||e.util.createCanvasElement().getContext("2d")),e._measuringContext},_splitText:function(){var t=this._splitTextIntoLines(this.text);return this.textLines=t.lines,this._textLines=t.graphemeLines,this._unwrappedTextLines=t._unwrappedLines,this._text=t.graphemeText,t},initDimensions:function(){this.__skipDimension||(this._splitText(),this._clearCache(),this.path?(this.width=this.path.width,this.height=this.path.height):(this.width=this.calcTextWidth()||this.cursorWidth||this.MIN_TEXT_WIDTH,this.height=this.calcTextHeight()),-1!==this.textAlign.indexOf("justify")&&this.enlargeSpaces(),this.saveState({propertySet:"_dimensionAffectingProps"}))},enlargeSpaces:function(){for(var t,e,i,n,r,s,o,a=0,c=this._textLines.length;c>a;a++)if(("justify"===this.textAlign||a!==c-1&&!this.isEndOfWrapping(a))&&(n=0,r=this._textLines[a],e=this.getLineWidth(a),e=l;l++)s=this.__charBounds[a][l],this._reSpaceAndTab.test(r[l])?(s.width+=t,s.kernedWidth+=t,s.left+=n,n+=t):s.left+=n}},isEndOfWrapping:function(t){return t===this._textLines.length-1},missingNewlineOffset:function(){return 1},toString:function(){return"#'},_getCacheCanvasDimensions:function(){var t=this.callSuper("_getCacheCanvasDimensions"),e=this.fontSize;return t.width+=e*t.zoomX,t.height+=e*t.zoomY,t},_render:function(t){var e=this.path;e&&!e.isNotVisible()&&e._render(t),this._setTextStyles(t),this._renderTextLinesBackground(t),this._renderTextDecoration(t,"underline"),this._renderText(t),this._renderTextDecoration(t,"overline"),this._renderTextDecoration(t,"linethrough")},_renderText:function(t){"stroke"===this.paintFirst?(this._renderTextStroke(t),this._renderTextFill(t)):(this._renderTextFill(t),this._renderTextStroke(t))},_setTextStyles:function(t,e,i){if(t.textBaseline="alphabetical",this.path)switch(this.pathAlign){case"center":t.textBaseline="middle";break;case"ascender":t.textBaseline="top";break;case"descender":t.textBaseline="bottom"}t.font=this._getFontDeclaration(e,i)},calcTextWidth:function(){for(var t=this.getLineWidth(0),e=1,i=this._textLines.length;i>e;e++){var n=this.getLineWidth(e);n>t&&(t=n)}return t},_renderTextLine:function(t,e,i,n,r,s){this._renderChars(t,e,i,n,r,s)},_renderTextLinesBackground:function(t){if(this.textBackgroundColor||this.styleHas("textBackgroundColor")){for(var e,i,n,r,s,o,a,c=t.fillStyle,l=this._getLeftOffset(),h=this._getTopOffset(),f=0,u=0,d=this.path,p=0,g=this._textLines.length;g>p;p++)if(e=this.getHeightOfLine(p),this.textBackgroundColor||this.styleHas("textBackgroundColor",p)){n=this._textLines[p],i=this._getLineLeftOffset(p),u=0,f=0,r=this.getValueOfPropertyAt(p,0,"textBackgroundColor");for(var m=0,y=n.length;y>m;m++)s=this.__charBounds[p][m],o=this.getValueOfPropertyAt(p,m,"textBackgroundColor"),d?(t.save(),t.translate(s.renderLeft,s.renderTop),t.rotate(s.angle),t.fillStyle=o,o&&t.fillRect(-s.width/2,-e/this.lineHeight*(1-this._fontSizeFraction),s.width,e/this.lineHeight),t.restore()):o!==r?(a=l+i+f,"rtl"===this.direction&&(a=this.width-a-u),t.fillStyle=r,r&&t.fillRect(a,h,u,e/this.lineHeight),f=s.left,u=s.width,r=o):u+=s.kernedWidth;o&&!d&&(a=l+i+f,"rtl"===this.direction&&(a=this.width-a-u),t.fillStyle=o,t.fillRect(a,h,u,e/this.lineHeight)),h+=e}else h+=e;t.fillStyle=c,this._removeShadow(t)}},getFontCache:function(t){var i=t.fontFamily.toLowerCase();e.charWidthsCache[i]||(e.charWidthsCache[i]={});var n=e.charWidthsCache[i],r=t.fontStyle.toLowerCase()+"_"+(t.fontWeight+"").toLowerCase();return n[r]||(n[r]={}),n[r]},_measureChar:function(t,e,i,n){var r,s,o,a,c=this.getFontCache(e),l=this._getFontDeclaration(e),h=this._getFontDeclaration(n),f=i+t,u=l===h,d=e.fontSize/this.CACHE_FONT_SIZE;if(i&&void 0!==c[i]&&(o=c[i]),void 0!==c[t]&&(a=r=c[t]),u&&void 0!==c[f]&&(s=c[f],a=s-o),void 0===r||void 0===o||void 0===s){var p=this.getMeasuringContext();this._setTextStyles(p,e,!0)}return void 0===r&&(a=r=p.measureText(t).width,c[t]=r),void 0===o&&u&&i&&(o=p.measureText(i).width,c[i]=o),u&&void 0===s&&(s=p.measureText(f).width,c[f]=s,a=s-o),{width:r*d,kernedWidth:a*d}},getHeightOfChar:function(t,e){return this.getValueOfPropertyAt(t,e,"fontSize")},measureLine:function(t){var e=this._measureLine(t);return 0!==this.charSpacing&&(e.width-=this._getWidthOfCharSpacing()),e.width<0&&(e.width=0),e},_measureLine:function(t){var i,n,r,s,o,a,c=0,l=this._textLines[t],h=0,f=new Array(l.length),u=0,d=this.path,p="right"===this.pathSide;for(this.__charBounds[t]=f,i=0;i=0:ia?u%=a:0>u&&(u+=a),this._setGraphemeOnPath(u,s,o),u+=s.kernedWidth}return{width:c,numOfSpaces:h}},_setGraphemeOnPath:function(t,i,n){var r=t+i.kernedWidth/2,s=this.path,o=e.util.getPointOnPath(s.path,r,s.segmentsInfo);i.renderLeft=o.x-n.x,i.renderTop=o.y-n.y,i.angle=o.angle+("right"===this.pathSide?Math.PI:0)},_getGraphemeBox:function(t,e,i,n,r){var s,o=this.getCompleteStyleDeclaration(e,i),a=n?this.getCompleteStyleDeclaration(e,i-1):{},c=this._measureChar(t,o,n,a),l=c.kernedWidth,h=c.width;0!==this.charSpacing&&(s=this._getWidthOfCharSpacing(),h+=s,l+=s);var f={width:h,left:0,height:o.fontSize,kernedWidth:l,deltaY:o.deltaY};if(i>0&&!r){var u=this.__charBounds[e][i-1];f.left=u.left+u.width+c.kernedWidth-c.width}return f},getHeightOfLine:function(t){if(this.__lineHeights[t])return this.__lineHeights[t];for(var e=this._textLines[t],i=this.getHeightOfChar(t,0),n=1,r=e.length;r>n;n++)i=Math.max(this.getHeightOfChar(t,n),i);return this.__lineHeights[t]=i*this.lineHeight*this._fontSizeMult},calcTextHeight:function(){for(var t,e=0,i=0,n=this._textLines.length;n>i;i++)t=this.getHeightOfLine(i),e+=i===n-1?t/this.lineHeight:t;return e},_getLeftOffset:function(){return"ltr"===this.direction?-this.width/2:this.width/2},_getTopOffset:function(){return-this.height/2},_renderTextCommon:function(t,e){t.save();for(var i=0,n=this._getLeftOffset(),r=this._getTopOffset(),s=0,o=this._textLines.length;o>s;s++){var a=this.getHeightOfLine(s),c=a/this.lineHeight,l=this._getLineLeftOffset(s);this._renderTextLine(e,t,this._textLines[s],n+l,r+i+c,s),i+=a}t.restore()},_renderTextFill:function(t){(this.fill||this.styleHas("fill"))&&this._renderTextCommon(t,"fillText")},_renderTextStroke:function(t){(this.stroke&&0!==this.strokeWidth||!this.isEmptyStyles())&&(this.shadow&&!this.shadow.affectStroke&&this._removeShadow(t),t.save(),this._setLineDash(t,this.strokeDashArray),t.beginPath(),this._renderTextCommon(t,"strokeText"),t.closePath(),t.restore())},_renderChars:function(t,i,n,r,s,o){var a,c,l,h,f,u=this.getHeightOfLine(o),d=-1!==this.textAlign.indexOf("justify"),p="",g=0,m=this.path,y=!d&&0===this.charSpacing&&this.isEmptyStyles(o)&&!m,v="ltr"===this.direction,x="ltr"===this.direction?1:-1,b=i.canvas.getAttribute("dir");if(i.save(),b!==this.direction&&(i.canvas.setAttribute("dir",v?"ltr":"rtl"),i.direction=v?"ltr":"rtl",i.textAlign=v?"left":"right"),s-=u*this._fontSizeFraction/this.lineHeight,y)return this._renderChar(t,i,o,0,n.join(""),r,s,u),void i.restore();for(var _=0,S=n.length-1;S>=_;_++)h=_===S||this.charSpacing||m,p+=n[_],l=this.__charBounds[o][_],0===g?(r+=x*(l.kernedWidth-l.width),g+=l.width):g+=l.kernedWidth,d&&!h&&this._reSpaceAndTab.test(n[_])&&(h=!0),h||(a=a||this.getCompleteStyleDeclaration(o,_),c=this.getCompleteStyleDeclaration(o,_+1),h=e.util.hasStyleChanged(a,c,!1)),h&&(m?(i.save(),i.translate(l.renderLeft,l.renderTop),i.rotate(l.angle),this._renderChar(t,i,o,_,p,-g/2,0,u),i.restore()):(f=r,this._renderChar(t,i,o,_,p,f,s,u)),p="",a=c,r+=x*g,g=0);i.restore()},_applyPatternGradientTransformText:function(t){var i,n=e.util.createCanvasElement(),r=this.width+this.strokeWidth,s=this.height+this.strokeWidth;return n.width=r,n.height=s,i=n.getContext("2d"),i.beginPath(),i.moveTo(0,0),i.lineTo(r,0),i.lineTo(r,s),i.lineTo(0,s),i.closePath(),i.translate(r/2,s/2),i.fillStyle=t.toLive(i),this._applyPatternGradientTransform(i,t),i.fill(),i.createPattern(n,"no-repeat")},handleFiller:function(t,e,i){var n,r;return i.toLive?"percentage"===i.gradientUnits||i.gradientTransform||i.patternTransform?(n=-this.width/2,r=-this.height/2,t.translate(n,r),t[e]=this._applyPatternGradientTransformText(i),{offsetX:n,offsetY:r}):(t[e]=i.toLive(t,this),this._applyPatternGradientTransform(t,i)):(t[e]=i,{offsetX:0,offsetY:0})},_setStrokeStyles:function(t,e){return t.lineWidth=e.strokeWidth,t.lineCap=this.strokeLineCap,t.lineDashOffset=this.strokeDashOffset,t.lineJoin=this.strokeLineJoin,t.miterLimit=this.strokeMiterLimit,this.handleFiller(t,"strokeStyle",e.stroke)},_setFillStyles:function(t,e){return this.handleFiller(t,"fillStyle",e.fill)},_renderChar:function(t,e,i,n,r,s,o){var a,c,l=this._getStyleDeclaration(i,n),h=this.getCompleteStyleDeclaration(i,n),f="fillText"===t&&h.fill,u="strokeText"===t&&h.stroke&&h.strokeWidth;(u||f)&&(e.save(),f&&(a=this._setFillStyles(e,h)),u&&(c=this._setStrokeStyles(e,h)),e.font=this._getFontDeclaration(h),l&&l.textBackgroundColor&&this._removeShadow(e),l&&l.deltaY&&(o+=l.deltaY),f&&e.fillText(r,s-a.offsetX,o-a.offsetY),u&&e.strokeText(r,s-c.offsetX,o-c.offsetY),e.restore())},setSuperscript:function(t,e){return this._setScript(t,e,this.superscript)},setSubscript:function(t,e){return this._setScript(t,e,this.subscript)},_setScript:function(t,e,i){var n=this.get2DCursorLocation(t,!0),r=this.getValueOfPropertyAt(n.lineIndex,n.charIndex,"fontSize"),s=this.getValueOfPropertyAt(n.lineIndex,n.charIndex,"deltaY"),o={fontSize:r*i.size,deltaY:s+r*i.baseline};return this.setSelectionStyles(o,t,e),this},_getLineLeftOffset:function(t){var e,i=this.getLineWidth(t),n=this.width-i,r=this.textAlign,s=this.direction,o=0,e=this.isEndOfWrapping(t);return"justify"===r||"justify-center"===r&&!e||"justify-right"===r&&!e||"justify-left"===r&&!e?0:("center"===r&&(o=n/2),"right"===r&&(o=n),"justify-center"===r&&(o=n/2),"justify-right"===r&&(o=n),"rtl"===s&&(o-=n),o)},_clearCache:function(){this.__lineWidths=[],this.__lineHeights=[],this.__charBounds=[]},_shouldClearDimensionCache:function(){var t=this._forceClearCache;return t||(t=this.hasStateChanged("_dimensionAffectingProps")),t&&(this.dirty=!0,this._forceClearCache=!1),t},getLineWidth:function(t){if(void 0!==this.__lineWidths[t])return this.__lineWidths[t];var e=this.measureLine(t),i=e.width;return this.__lineWidths[t]=i,i},_getWidthOfCharSpacing:function(){return 0!==this.charSpacing?this.fontSize*this.charSpacing/1e3:0},getValueOfPropertyAt:function(t,e,i){var n=this._getStyleDeclaration(t,e);return n&&"undefined"!=typeof n[i]?n[i]:this[i]},_renderTextDecoration:function(t,e){if(this[e]||this.styleHas(e)){for(var i,n,r,s,o,a,c,l,h,f,u,d,p,g,m,y,v=this._getLeftOffset(),x=this._getTopOffset(),b=this.path,_=this._getWidthOfCharSpacing(),S=this.offsets[e],C=0,T=this._textLines.length;T>C;C++)if(i=this.getHeightOfLine(C),this[e]||this.styleHas(e,C)){c=this._textLines[C],g=i/this.lineHeight,s=this._getLineLeftOffset(C),f=0,u=0,l=this.getValueOfPropertyAt(C,0,e),y=this.getValueOfPropertyAt(C,0,"fill"),h=x+g*(1-this._fontSizeFraction),n=this.getHeightOfChar(C,0),o=this.getValueOfPropertyAt(C,0,"deltaY");for(var w=0,O=c.length;O>w;w++)if(d=this.__charBounds[C][w],p=this.getValueOfPropertyAt(C,w,e),m=this.getValueOfPropertyAt(C,w,"fill"),r=this.getHeightOfChar(C,w),a=this.getValueOfPropertyAt(C,w,"deltaY"),b&&p&&m)t.save(),t.fillStyle=y,t.translate(d.renderLeft,d.renderTop),t.rotate(d.angle),t.fillRect(-d.kernedWidth/2,S*r+a,d.kernedWidth,this.fontSize/15),t.restore();else if((p!==l||m!==y||r!==n||a!==o)&&u>0){var k=v+s+f;"rtl"===this.direction&&(k=this.width-k-u),l&&y&&(t.fillStyle=y,t.fillRect(k,h+S*n+o,u,this.fontSize/15)),f=d.left,u=d.width,l=p,y=m,n=r,o=a}else u+=d.kernedWidth;var k=v+s+f;"rtl"===this.direction&&(k=this.width-k-u),t.fillStyle=m,p&&m&&t.fillRect(k,h+S*n+o,u-_,this.fontSize/15),x+=i}else x+=i;this._removeShadow(t)}},_getFontDeclaration:function(t,i){var n=t||this,r=this.fontFamily,s=e.Text.genericFonts.indexOf(r.toLowerCase())>-1,o=void 0===r||r.indexOf("'")>-1||r.indexOf(",")>-1||r.indexOf('"')>-1||s?n.fontFamily:'"'+n.fontFamily+'"';return[e.isLikelyNode?n.fontWeight:n.fontStyle,e.isLikelyNode?n.fontStyle:n.fontWeight,i?this.CACHE_FONT_SIZE+"px":n.fontSize+"px",o].join(" ")},render:function(t){this.visible&&(!this.canvas||!this.canvas.skipOffscreen||this.group||this.isOnScreen())&&(this._shouldClearDimensionCache()&&this.initDimensions(),this.callSuper("render",t))},_splitTextIntoLines:function(t){for(var i=t.split(this._reNewline),n=new Array(i.length),r=["\n"],s=[],o=0;or;r++){if(t<=i[r].length)return{lineIndex:r,charIndex:t};t-=i[r].length+this.missingNewlineOffset(r)}return{lineIndex:r-1,charIndex:i[r-1].lengthr;r++)n.push(this.getStyleAtPosition(r,i));return n},getStyleAtPosition:function(t,e){var i=this.get2DCursorLocation(t),n=e?this.getCompleteStyleDeclaration(i.lineIndex,i.charIndex):this._getStyleDeclaration(i.lineIndex,i.charIndex);return n||{}},setSelectionStyles:function(t,e,i){"undefined"==typeof e&&(e=this.selectionStart||0),"undefined"==typeof i&&(i=this.selectionEnd||e);for(var n=e;i>n;n++)this._extendStyles(n,t);return this._forceClearCache=!0,this},_getStyleDeclaration:function(t,e){var i=this.styles&&this.styles[t];return i?i[e]:null},getCompleteStyleDeclaration:function(t,e){for(var i,n=this._getStyleDeclaration(t,e)||{},r={},s=0;s-1&&(t.underline=!0),t.textDecoration.indexOf("line-through")>-1&&(t.linethrough=!0),t.textDecoration.indexOf("overline")>-1&&(t.overline=!0),delete t.textDecoration)}fabric.IText=fabric.util.createClass(fabric.Text,fabric.Observable,{type:"i-text",selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"",cursorDelay:1e3,cursorDuration:600,caching:!0,hiddenTextareaContainer:null,_reSpace:/\s|\n/,_currentCursorOpacity:0,_selectionDirection:null,_abortCursorAnimation:!1,__widthOfSpace:[],inCompositionMode:!1,initialize:function(t,e){this.callSuper("initialize",t,e),this.initBehavior()},setSelectionStart:function(t){t=Math.max(t,0),this._updateAndFire("selectionStart",t)},setSelectionEnd:function(t){t=Math.min(t,this.text.length),this._updateAndFire("selectionEnd",t)},_updateAndFire:function(t,e){this[t]!==e&&(this._fireSelectionChanged(),this[t]=e),this._updateTextarea()},_fireSelectionChanged:function(){this.fire("selection:changed"),this.canvas&&this.canvas.fire("text:selection:changed",{target:this})},initDimensions:function(){this.isEditing&&this.initDelayedCursor(),this.clearContextTop(),this.callSuper("initDimensions")},render:function(t){this.clearContextTop(),this.callSuper("render",t),this.cursorOffsetCache={},this.renderCursorOrSelection()},_render:function(t){this.callSuper("_render",t)},clearContextTop:function(t){if(this.isEditing&&this.canvas&&this.canvas.contextTop){var e=this.canvas.contextTop,i=this.canvas.viewportTransform;e.save(),e.transform(i[0],i[1],i[2],i[3],i[4],i[5]),this.transform(e),this._clearTextArea(e),t||e.restore()}},renderCursorOrSelection:function(){if(this.isEditing&&this.canvas&&this.canvas.contextTop){var t=this._getCursorBoundaries(),e=this.canvas.contextTop;this.clearContextTop(!0),this.selectionStart===this.selectionEnd?this.renderCursor(t,e):this.renderSelection(t,e),e.restore()}},_clearTextArea:function(t){var e=this.width+4,i=this.height+4;t.clearRect(-e/2,-i/2,e,i)},_getCursorBoundaries:function(t){"undefined"==typeof t&&(t=this.selectionStart);var e=this._getLeftOffset(),i=this._getTopOffset(),n=this._getCursorBoundariesOffsets(t);return{left:e,top:i,leftOffset:n.left,topOffset:n.top}},_getCursorBoundariesOffsets:function(t){if(this.cursorOffsetCache&&"top"in this.cursorOffsetCache)return this.cursorOffsetCache;var e,i,n,r,s=0,o=0,a=this.get2DCursorLocation(t);n=a.charIndex,i=a.lineIndex;for(var c=0;i>c;c++)s+=this.getHeightOfLine(c);e=this._getLineLeftOffset(i);var l=this.__charBounds[i][n];return l&&(o=l.left),0!==this.charSpacing&&n===this._textLines[i].length&&(o-=this._getWidthOfCharSpacing()),r={top:s,left:e+(o>0?o:0)},"rtl"===this.direction&&(r.left*=-1),this.cursorOffsetCache=r,this.cursorOffsetCache},renderCursor:function(t,e){var i=this.get2DCursorLocation(),n=i.lineIndex,r=i.charIndex>0?i.charIndex-1:0,s=this.getValueOfPropertyAt(n,r,"fontSize"),o=this.scaleX*this.canvas.getZoom(),a=this.cursorWidth/o,c=t.topOffset,l=this.getValueOfPropertyAt(n,r,"deltaY");c+=(1-this._fontSizeFraction)*this.getHeightOfLine(n)/this.lineHeight-s*(1-this._fontSizeFraction),this.inCompositionMode&&this.renderSelection(t,e),e.fillStyle=this.cursorColor||this.getValueOfPropertyAt(n,r,"fill"),e.globalAlpha=this.__isMousedown?1:this._currentCursorOpacity,e.fillRect(t.left+t.leftOffset-a/2,c+t.top+l,a,s)},renderSelection:function(t,e){for(var i=this.inCompositionMode?this.hiddenTextarea.selectionStart:this.selectionStart,n=this.inCompositionMode?this.hiddenTextarea.selectionEnd:this.selectionEnd,r=-1!==this.textAlign.indexOf("justify"),s=this.get2DCursorLocation(i),o=this.get2DCursorLocation(n),a=s.lineIndex,c=o.lineIndex,l=s.charIndex<0?0:s.charIndex,h=o.charIndex<0?0:o.charIndex,u=a;c>=u;u++){var f=this._getLineLeftOffset(u)||0,d=this.getHeightOfLine(u),p=0,g=0,m=0;if(u===a&&(g=this.__charBounds[a][l].left),u>=a&&c>u)m=r&&!this.isEndOfWrapping(u)?this.width:this.getLineWidth(u)||5;else if(u===c)if(0===h)m=this.__charBounds[c][h].left;else{var v=this._getWidthOfCharSpacing();m=this.__charBounds[c][h-1].left+this.__charBounds[c][h-1].width-v}p=d,(this.lineHeight<1||u===c&&this.lineHeight>1)&&(d/=this.lineHeight);var y=t.left+f+g,b=m-g,x=d,_=0;this.inCompositionMode?(e.fillStyle=this.compositionColor||"black",x=1,_=d):e.fillStyle=this.selectionColor,"rtl"===this.direction&&(y=this.width-y-b),e.fillRect(y,t.top+t.topOffset+_,b,x),t.topOffset+=p}},getCurrentCharFontSize:function(){var t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,"fontSize")},getCurrentCharColor:function(){var t=this._getCurrentCharIndex();return this.getValueOfPropertyAt(t.l,t.c,"fill")},_getCurrentCharIndex:function(){var t=this.get2DCursorLocation(this.selectionStart,!0),e=t.charIndex>0?t.charIndex-1:0;return{l:t.lineIndex,c:e}}}),fabric.IText.fromObject=function(e,i){if(e.styles=fabric.util.stylesFromArray(e.styles,e.text),t(e),e.styles)for(var n in e.styles)for(var r in e.styles[n])t(e.styles[n][r]);fabric.Object._fromObject("IText",e,i,"text")}}();!function(){var t=fabric.util.object.clone;fabric.util.object.extend(fabric.IText.prototype,{initBehavior:function(){this.initAddedHandler(),this.initRemovedHandler(),this.initCursorSelectionHandlers(),this.initDoubleClickSimulation(),this.mouseMoveHandler=this.mouseMoveHandler.bind(this)},onDeselect:function(){this.isEditing&&this.exitEditing(),this.selected=!1},initAddedHandler:function(){var t=this;this.on("added",function(){var e=t.canvas;e&&(e._hasITextHandlers||(e._hasITextHandlers=!0,t._initCanvasHandlers(e)),e._iTextInstances=e._iTextInstances||[],e._iTextInstances.push(t))})},initRemovedHandler:function(){var t=this;this.on("removed",function(){var e=t.canvas;e&&(e._iTextInstances=e._iTextInstances||[],fabric.util.removeFromArray(e._iTextInstances,t),0===e._iTextInstances.length&&(e._hasITextHandlers=!1,t._removeCanvasHandlers(e)))})},_initCanvasHandlers:function(t){t._mouseUpITextHandler=function(){t._iTextInstances&&t._iTextInstances.forEach(function(t){t.__isMousedown=!1})},t.on("mouse:up",t._mouseUpITextHandler)},_removeCanvasHandlers:function(t){t.off("mouse:up",t._mouseUpITextHandler)},_tick:function(){this._currentTickState=this._animateCursor(this,1,this.cursorDuration,"_onTickComplete")},_animateCursor:function(t,e,i,n){var r;return r={isAborted:!1,abort:function(){this.isAborted=!0}},t.animate("_currentCursorOpacity",e,{duration:i,onComplete:function(){r.isAborted||t[n]()},onChange:function(){t.canvas&&t.selectionStart===t.selectionEnd&&t.renderCursorOrSelection()},abort:function(){return r.isAborted}}),r},_onTickComplete:function(){var t=this;this._cursorTimeout1&&clearTimeout(this._cursorTimeout1),this._cursorTimeout1=setTimeout(function(){t._currentTickCompleteState=t._animateCursor(t,0,this.cursorDuration/2,"_tick")},100)},initDelayedCursor:function(t){var e=this,i=t?0:this.cursorDelay;this.abortCursorAnimation(),this._currentCursorOpacity=1,this._cursorTimeout2=setTimeout(function(){e._tick()},i)},abortCursorAnimation:function(){var t=this._currentTickState||this._currentTickCompleteState,e=this.canvas;this._currentTickState&&this._currentTickState.abort(),this._currentTickCompleteState&&this._currentTickCompleteState.abort(),clearTimeout(this._cursorTimeout1),clearTimeout(this._cursorTimeout2),this._currentCursorOpacity=0,t&&e&&e.clearContext(e.contextTop||e.contextContainer)},selectAll:function(){return this.selectionStart=0,this.selectionEnd=this._text.length,this._fireSelectionChanged(),this._updateTextarea(),this},getSelectedText:function(){return this._text.slice(this.selectionStart,this.selectionEnd).join("")},findWordBoundaryLeft:function(t){var e=0,i=t-1;if(this._reSpace.test(this._text[i]))for(;this._reSpace.test(this._text[i]);)e++,i--;for(;/\S/.test(this._text[i])&&i>-1;)e++,i--;return t-e},findWordBoundaryRight:function(t){var e=0,i=t;if(this._reSpace.test(this._text[i]))for(;this._reSpace.test(this._text[i]);)e++,i++;for(;/\S/.test(this._text[i])&&i-1;)e++,i--;return t-e},findLineBoundaryRight:function(t){for(var e=0,i=t;!/\n/.test(this._text[i])&&i0&&nthis.__selectionStartOnMouseDown?(this.selectionStart=this.__selectionStartOnMouseDown,this.selectionEnd=e):(this.selectionStart=e,this.selectionEnd=this.__selectionStartOnMouseDown),(this.selectionStart!==i||this.selectionEnd!==n)&&(this.restartCursorIfNeeded(),this._fireSelectionChanged(),this._updateTextarea(),this.renderCursorOrSelection()))}},_setEditingProps:function(){this.hoverCursor="text",this.canvas&&(this.canvas.defaultCursor=this.canvas.moveCursor="text"),this.borderColor=this.editingBorderColor,this.hasControls=this.selectable=!1,this.lockMovementX=this.lockMovementY=!0},fromStringToGraphemeSelection:function(t,e,i){var n=i.slice(0,t),r=fabric.util.string.graphemeSplit(n).length;if(t===e)return{selectionStart:r,selectionEnd:r};var s=i.slice(t,e),o=fabric.util.string.graphemeSplit(s).length;return{selectionStart:r,selectionEnd:r+o}},fromGraphemeToStringSelection:function(t,e,i){var n=i.slice(0,t),r=n.join("").length;if(t===e)return{selectionStart:r,selectionEnd:r};var s=i.slice(t,e),o=s.join("").length;return{selectionStart:r,selectionEnd:r+o}},_updateTextarea:function(){if(this.cursorOffsetCache={},this.hiddenTextarea){if(!this.inCompositionMode){var t=this.fromGraphemeToStringSelection(this.selectionStart,this.selectionEnd,this._text);this.hiddenTextarea.selectionStart=t.selectionStart,this.hiddenTextarea.selectionEnd=t.selectionEnd}this.updateTextareaPosition()}},updateFromTextArea:function(){if(this.hiddenTextarea){this.cursorOffsetCache={},this.text=this.hiddenTextarea.value,this._shouldClearDimensionCache()&&(this.initDimensions(),this.setCoords());var t=this.fromStringToGraphemeSelection(this.hiddenTextarea.selectionStart,this.hiddenTextarea.selectionEnd,this.hiddenTextarea.value);this.selectionEnd=this.selectionStart=t.selectionEnd,this.inCompositionMode||(this.selectionStart=t.selectionStart),this.updateTextareaPosition()}},updateTextareaPosition:function(){if(this.selectionStart===this.selectionEnd){var t=this._calcTextareaPosition();this.hiddenTextarea.style.left=t.left,this.hiddenTextarea.style.top=t.top}},_calcTextareaPosition:function(){if(!this.canvas)return{x:1,y:1};var t=this.inCompositionMode?this.compositionStart:this.selectionStart,e=this._getCursorBoundaries(t),i=this.get2DCursorLocation(t),n=i.lineIndex,r=i.charIndex,s=this.getValueOfPropertyAt(n,r,"fontSize")*this.lineHeight,o=e.leftOffset,a=this.calcTransformMatrix(),c={x:e.left+o,y:e.top+e.topOffset+s},l=this.canvas.getRetinaScaling(),h=this.canvas.upperCanvasEl,u=h.width/l,f=h.height/l,d=u-s,p=f-s,m=h.clientWidth/u,g=h.clientHeight/f;return c=fabric.util.transformPoint(c,a),c=fabric.util.transformPoint(c,this.canvas.viewportTransform),c.x*=m,c.y*=g,c.x<0&&(c.x=0),c.x>d&&(c.x=d),c.y<0&&(c.y=0),c.y>p&&(c.y=p),c.x+=this.canvas._offset.left,c.y+=this.canvas._offset.top,{left:c.x+"px",top:c.y+"px",fontSize:s+"px",charHeight:s}},_saveEditingProps:function(){this._savedProps={hasControls:this.hasControls,borderColor:this.borderColor,lockMovementX:this.lockMovementX,lockMovementY:this.lockMovementY,hoverCursor:this.hoverCursor,selectable:this.selectable,defaultCursor:this.canvas&&this.canvas.defaultCursor,moveCursor:this.canvas&&this.canvas.moveCursor}},_restoreEditingProps:function(){this._savedProps&&(this.hoverCursor=this._savedProps.hoverCursor,this.hasControls=this._savedProps.hasControls,this.borderColor=this._savedProps.borderColor,this.selectable=this._savedProps.selectable,this.lockMovementX=this._savedProps.lockMovementX,this.lockMovementY=this._savedProps.lockMovementY,this.canvas&&(this.canvas.defaultCursor=this._savedProps.defaultCursor,this.canvas.moveCursor=this._savedProps.moveCursor))},exitEditing:function(){var t=this._textBeforeEdit!==this.text,e=this.hiddenTextarea;return this.selected=!1,this.isEditing=!1,this.selectionEnd=this.selectionStart,e&&(e.blur&&e.blur(),e.parentNode&&e.parentNode.removeChild(e)),this.hiddenTextarea=null,this.abortCursorAnimation(),this._restoreEditingProps(),this._currentCursorOpacity=0,this._shouldClearDimensionCache()&&(this.initDimensions(),this.setCoords()),this.fire("editing:exited"),t&&this.fire("modified"),this.canvas&&(this.canvas.off("mouse:move",this.mouseMoveHandler),this.canvas.fire("text:editing:exited",{target:this}),t&&this.canvas.fire("object:modified",{target:this})),this},_removeExtraneousStyles:function(){for(var t in this.styles)this._textLines[t]||delete this.styles[t]},removeStyleFromTo:function(t,e){var i,n,r=this.get2DCursorLocation(t,!0),s=this.get2DCursorLocation(e,!0),o=r.lineIndex,a=r.charIndex,c=s.lineIndex,l=s.charIndex;if(o!==c){if(this.styles[o])for(i=a;i=i;i++)delete this.styles[i];this.shiftLineStyles(c,o-c)}else if(this.styles[o]){n=this.styles[o];var h,u,f=l-a;for(i=a;l>i;i++)delete n[i];for(u in this.styles[o])h=parseInt(u,10),h>=l&&(n[h-f]=n[u],delete n[u])}},shiftLineStyles:function(e,i){var n=t(this.styles);for(var r in this.styles){var s=parseInt(r,10);s>e&&(this.styles[s+i]=n[s],n[s-i]||delete this.styles[s])}},restartCursorIfNeeded:function(){(!this._currentTickState||this._currentTickState.isAborted||!this._currentTickCompleteState||this._currentTickCompleteState.isAborted)&&this.initDelayedCursor()},insertNewlineStyleObject:function(e,i,n,r){var s,o={},a=!1,c=this._unwrappedTextLines[e].length===i;n||(n=1),this.shiftLineStyles(e,n),this.styles[e]&&(s=this.styles[e][0===i?i:i-1]);for(var l in this.styles[e]){var h=parseInt(l,10);h>=i&&(a=!0,o[h-i]=this.styles[e][l],c&&0===i||delete this.styles[e][l])}var u=!1;for(a&&!c&&(this.styles[e+n]=o,u=!0),u&&n--;n>0;)r&&r[n-1]?this.styles[e+n]={0:t(r[n-1])}:s?this.styles[e+n]={0:t(s)}:delete this.styles[e+n],n--;this._forceClearCache=!0},insertCharStyleObject:function(e,i,n,r){this.styles||(this.styles={});var s=this.styles[e],o=s?t(s):{};n||(n=1);for(var a in o){var c=parseInt(a,10);c>=i&&(s[c+n]=o[c],o[c-n]||delete s[c])}if(this._forceClearCache=!0,r)for(;n--;)Object.keys(r[n]).length&&(this.styles[e]||(this.styles[e]={}),this.styles[e][i+n]=t(r[n]));else if(s)for(var l=s[i?i-1:1];l&&n--;)this.styles[e][i+n]=t(l)},insertNewStyleBlock:function(t,e,i){for(var n=this.get2DCursorLocation(e,!0),r=[0],s=0,o=0;o0&&(this.insertCharStyleObject(n.lineIndex,n.charIndex,r[0],i),i=i&&i.slice(r[0]+1)),s&&this.insertNewlineStyleObject(n.lineIndex,n.charIndex+r[0],s);for(var o=1;s>o;o++)r[o]>0?this.insertCharStyleObject(n.lineIndex+o,0,r[o],i):i&&this.styles[n.lineIndex+o]&&i[0]&&(this.styles[n.lineIndex+o][0]=i[0]),i=i&&i.slice(r[o]+1);r[o]>0&&this.insertCharStyleObject(n.lineIndex+o,0,r[o],i)},setSelectionStartEndWithShift:function(t,e,i){t>=i?(e===t?this._selectionDirection="left":"right"===this._selectionDirection&&(this._selectionDirection="left",this.selectionEnd=t),this.selectionStart=i):i>t&&e>i?"right"===this._selectionDirection?this.selectionEnd=i:this.selectionStart=i:(e===t?this._selectionDirection="right":"left"===this._selectionDirection&&(this._selectionDirection="right",this.selectionStart=e),this.selectionEnd=i)},setSelectionInBoundaries:function(){var t=this.text.length;this.selectionStart>t?this.selectionStart=t:this.selectionStart<0&&(this.selectionStart=0),this.selectionEnd>t?this.selectionEnd=t:this.selectionEnd<0&&(this.selectionEnd=0)}})}();fabric.util.object.extend(fabric.IText.prototype,{initDoubleClickSimulation:function(){this.__lastClickTime=+new Date,this.__lastLastClickTime=+new Date,this.__lastPointer={},this.on("mousedown",this.onMouseDown)},onMouseDown:function(t){if(this.canvas){this.__newClickTime=+new Date;var e=t.pointer;this.isTripleClick(e)&&(this.fire("tripleclick",t),this._stopEvent(t.e)),this.__lastLastClickTime=this.__lastClickTime,this.__lastClickTime=this.__newClickTime,this.__lastPointer=e,this.__lastIsEditing=this.isEditing,this.__lastSelected=this.selected}},isTripleClick:function(t){return this.__newClickTime-this.__lastClickTime<500&&this.__lastClickTime-this.__lastLastClickTime<500&&this.__lastPointer.x===t.x&&this.__lastPointer.y===t.y},_stopEvent:function(t){t.preventDefault&&t.preventDefault(),t.stopPropagation&&t.stopPropagation()},initCursorSelectionHandlers:function(){this.initMousedownHandler(),this.initMouseupHandler(),this.initClicks()},doubleClickHandler:function(t){this.isEditing&&this.selectWord(this.getSelectionStartFromPointer(t.e))},tripleClickHandler:function(t){this.isEditing&&this.selectLine(this.getSelectionStartFromPointer(t.e))},initClicks:function(){this.on("mousedblclick",this.doubleClickHandler),this.on("tripleclick",this.tripleClickHandler)},_mouseDownHandler:function(t){!this.canvas||!this.editable||t.e.button&&1!==t.e.button||(this.__isMousedown=!0,this.selected&&(this.inCompositionMode=!1,this.setCursorByClick(t.e)),this.isEditing&&(this.__selectionStartOnMouseDown=this.selectionStart,this.selectionStart===this.selectionEnd&&this.abortCursorAnimation(),this.renderCursorOrSelection()))},_mouseDownHandlerBefore:function(t){!this.canvas||!this.editable||t.e.button&&1!==t.e.button||(this.selected=this===this.canvas._activeObject)},initMousedownHandler:function(){this.on("mousedown",this._mouseDownHandler),this.on("mousedown:before",this._mouseDownHandlerBefore)},initMouseupHandler:function(){this.on("mouseup",this.mouseUpHandler)},mouseUpHandler:function(t){if(this.__isMousedown=!1,!(!this.editable||this.group||t.transform&&t.transform.actionPerformed||t.e.button&&1!==t.e.button)){if(this.canvas){var e=this.canvas._activeObject;if(e&&e!==this)return}this.__lastSelected&&!this.__corner?(this.selected=!1,this.__lastSelected=!1,this.enterEditing(t.e),this.selectionStart===this.selectionEnd?this.initDelayedCursor(!0):this.renderCursorOrSelection()):this.selected=!0}},setCursorByClick:function(t){var e=this.getSelectionStartFromPointer(t),i=this.selectionStart,n=this.selectionEnd;t.shiftKey?this.setSelectionStartEndWithShift(i,n,e):(this.selectionStart=e,this.selectionEnd=e),this.isEditing&&(this._fireSelectionChanged(),this._updateTextarea())},getSelectionStartFromPointer:function(t){for(var e,i,n=this.getLocalPointer(t),r=0,a=0,o=0,s=0,c=0,l=0,h=this._textLines.length;h>l&&o<=n.y;l++)o+=this.getHeightOfLine(l)*this.scaleY,c=l,l>0&&(s+=this._textLines[l-1].length+this.missingNewlineOffset(l-1));e=this._getLineLeftOffset(c),a=e*this.scaleX,i=this._textLines[c],"rtl"===this.direction&&(n.x=this.width*this.scaleX-n.x+a);for(var f=0,u=i.length;u>f&&(r=a,a+=this.__charBounds[c][f].kernedWidth*this.scaleX,a<=n.x);f++)s++;return this._getNewSelectionStartFromOffset(n,r,a,s,u)},_getNewSelectionStartFromOffset:function(t,e,i,n,r){var a=t.x-e,o=i-t.x,s=o>a||0>o?0:1,c=n+s;return this.flipX&&(c=r-c),c>this._text.length&&(c=this._text.length),c}});fabric.util.object.extend(fabric.IText.prototype,{initHiddenTextarea:function(){this.hiddenTextarea=fabric.document.createElement("textarea"),this.hiddenTextarea.setAttribute("autocapitalize","off"),this.hiddenTextarea.setAttribute("autocorrect","off"),this.hiddenTextarea.setAttribute("autocomplete","off"),this.hiddenTextarea.setAttribute("spellcheck","false"),this.hiddenTextarea.setAttribute("data-fabric-hiddentextarea",""),this.hiddenTextarea.setAttribute("wrap","off");var t=this._calcTextareaPosition();this.hiddenTextarea.style.cssText="position: absolute; top: "+t.top+"; left: "+t.left+"; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px; paddingーtop: "+t.fontSize+";",this.hiddenTextareaContainer?this.hiddenTextareaContainer.appendChild(this.hiddenTextarea):fabric.document.body.appendChild(this.hiddenTextarea),fabric.util.addListener(this.hiddenTextarea,"keydown",this.onKeyDown.bind(this)),fabric.util.addListener(this.hiddenTextarea,"keyup",this.onKeyUp.bind(this)),fabric.util.addListener(this.hiddenTextarea,"input",this.onInput.bind(this)),fabric.util.addListener(this.hiddenTextarea,"copy",this.copy.bind(this)),fabric.util.addListener(this.hiddenTextarea,"cut",this.copy.bind(this)),fabric.util.addListener(this.hiddenTextarea,"paste",this.paste.bind(this)),fabric.util.addListener(this.hiddenTextarea,"compositionstart",this.onCompositionStart.bind(this)),fabric.util.addListener(this.hiddenTextarea,"compositionupdate",this.onCompositionUpdate.bind(this)),fabric.util.addListener(this.hiddenTextarea,"compositionend",this.onCompositionEnd.bind(this)),!this._clickHandlerInitialized&&this.canvas&&(fabric.util.addListener(this.canvas.upperCanvasEl,"click",this.onClick.bind(this)),this._clickHandlerInitialized=!0)},keysMap:{9:"exitEditing",27:"exitEditing",33:"moveCursorUp",34:"moveCursorDown",35:"moveCursorRight",36:"moveCursorLeft",37:"moveCursorLeft",38:"moveCursorUp",39:"moveCursorRight",40:"moveCursorDown"},keysMapRtl:{9:"exitEditing",27:"exitEditing",33:"moveCursorUp",34:"moveCursorDown",35:"moveCursorLeft",36:"moveCursorRight",37:"moveCursorRight",38:"moveCursorUp",39:"moveCursorLeft",40:"moveCursorDown"},ctrlKeysMapUp:{67:"copy",88:"cut"},ctrlKeysMapDown:{65:"selectAll"},onClick:function(){this.hiddenTextarea&&this.hiddenTextarea.focus()},onKeyDown:function(t){if(this.isEditing){var e="rtl"===this.direction?this.keysMapRtl:this.keysMap;if(t.keyCode in e)this[e[t.keyCode]](t);else{if(!(t.keyCode in this.ctrlKeysMapDown&&(t.ctrlKey||t.metaKey)))return;this[this.ctrlKeysMapDown[t.keyCode]](t)}t.stopImmediatePropagation(),t.preventDefault(),t.keyCode>=33&&t.keyCode<=40?(this.inCompositionMode=!1,this.clearContextTop(),this.renderCursorOrSelection()):this.canvas&&this.canvas.requestRenderAll()}},onKeyUp:function(t){return!this.isEditing||this._copyDone||this.inCompositionMode?void(this._copyDone=!1):void(t.keyCode in this.ctrlKeysMapUp&&(t.ctrlKey||t.metaKey)&&(this[this.ctrlKeysMapUp[t.keyCode]](t),t.stopImmediatePropagation(),t.preventDefault(),this.canvas&&this.canvas.requestRenderAll()))},onInput:function(t){var e=this.fromPaste;if(this.fromPaste=!1,t&&t.stopPropagation(),this.isEditing){var i,n,r,a,o,s=this._splitTextIntoLines(this.hiddenTextarea.value).graphemeText,c=this._text.length,l=s.length,h=l-c,f=this.selectionStart,u=this.selectionEnd,d=f!==u;if(""===this.hiddenTextarea.value)return this.styles={},this.updateFromTextArea(),this.fire("changed"),void(this.canvas&&(this.canvas.fire("text:changed",{target:this}),this.canvas.requestRenderAll()));var p=this.fromStringToGraphemeSelection(this.hiddenTextarea.selectionStart,this.hiddenTextarea.selectionEnd,this.hiddenTextarea.value),g=f>p.selectionStart;d?(i=this._text.slice(f,u),h+=u-f):c>l&&(i=g?this._text.slice(u+h,u):this._text.slice(f,f-h)),n=s.slice(p.selectionEnd-h,p.selectionEnd),i&&i.length&&(n.length&&(r=this.getSelectionStyles(f,f+1,!1),r=n.map(function(){return r[0]})),d?(a=f,o=u):g?(a=u-i.length,o=u):(a=u,o=u+i.length),this.removeStyleFromTo(a,o)),n.length&&(e&&n.join("")===fabric.copiedText&&!fabric.disableStyleCopyPaste&&(r=fabric.copiedTextStyle),this.insertNewStyleBlock(n,f,r)),this.updateFromTextArea(),this.fire("changed"),this.canvas&&(this.canvas.fire("text:changed",{target:this}),this.canvas.requestRenderAll())}},onCompositionStart:function(){this.inCompositionMode=!0},onCompositionEnd:function(){this.inCompositionMode=!1},onCompositionUpdate:function(t){this.compositionStart=t.target.selectionStart,this.compositionEnd=t.target.selectionEnd,this.updateTextareaPosition()},copy:function(){this.selectionStart!==this.selectionEnd&&(fabric.copiedText=this.getSelectedText(),fabric.copiedTextStyle=fabric.disableStyleCopyPaste?null:this.getSelectionStyles(this.selectionStart,this.selectionEnd,!0),this._copyDone=!0)},paste:function(){this.fromPaste=!0},_getClipboardData:function(t){return t&&t.clipboardData||fabric.window.clipboardData},_getWidthBeforeCursor:function(t,e){var i,n=this._getLineLeftOffset(t);return e>0&&(i=this.__charBounds[t][e-1],n+=i.left+i.width),n},getDownCursorOffset:function(t,e){var i=this._getSelectionForOffset(t,e),n=this.get2DCursorLocation(i),r=n.lineIndex;if(r===this._textLines.length-1||t.metaKey||34===t.keyCode)return this._text.length-i;var a=n.charIndex,o=this._getWidthBeforeCursor(r,a),s=this._getIndexOnLine(r+1,o),c=this._textLines[r].slice(a);return c.length+s+1+this.missingNewlineOffset(r)},_getSelectionForOffset:function(t,e){return t.shiftKey&&this.selectionStart!==this.selectionEnd&&e?this.selectionEnd:this.selectionStart},getUpCursorOffset:function(t,e){var i=this._getSelectionForOffset(t,e),n=this.get2DCursorLocation(i),r=n.lineIndex;if(0===r||t.metaKey||33===t.keyCode)return-i;var a=n.charIndex,o=this._getWidthBeforeCursor(r,a),s=this._getIndexOnLine(r-1,o),c=this._textLines[r].slice(0,a),l=this.missingNewlineOffset(r-1);return-this._textLines[r-1].length+s-c.length+(1-l)},_getIndexOnLine:function(t,e){for(var i,n,r=this._textLines[t],a=this._getLineLeftOffset(t),o=a,s=0,c=0,l=r.length;l>c;c++)if(i=this.__charBounds[t][c].width,o+=i,o>e){n=!0;var h=o-i,f=o,u=Math.abs(h-e),d=Math.abs(f-e);s=u>d?c:c-1;break}return n||(s=r.length-1),s},moveCursorDown:function(t){this.selectionStart>=this._text.length&&this.selectionEnd>=this._text.length||this._moveCursorUpOrDown("Down",t)},moveCursorUp:function(t){(0!==this.selectionStart||0!==this.selectionEnd)&&this._moveCursorUpOrDown("Up",t)},_moveCursorUpOrDown:function(t,e){var i="get"+t+"CursorOffset",n=this[i](e,"right"===this._selectionDirection);e.shiftKey?this.moveCursorWithShift(n):this.moveCursorWithoutShift(n),0!==n&&(this.setSelectionInBoundaries(),this.abortCursorAnimation(),this._currentCursorOpacity=1,this.initDelayedCursor(),this._fireSelectionChanged(),this._updateTextarea())},moveCursorWithShift:function(t){var e="left"===this._selectionDirection?this.selectionStart+t:this.selectionEnd+t;return this.setSelectionStartEndWithShift(this.selectionStart,this.selectionEnd,e),0!==t},moveCursorWithoutShift:function(t){return 0>t?(this.selectionStart+=t,this.selectionEnd=this.selectionStart):(this.selectionEnd+=t,this.selectionStart=this.selectionEnd),0!==t},moveCursorLeft:function(t){(0!==this.selectionStart||0!==this.selectionEnd)&&this._moveCursorLeftOrRight("Left",t)},_move:function(t,e,i){var n;if(t.altKey)n=this["findWordBoundary"+i](this[e]);else{if(!t.metaKey&&35!==t.keyCode&&36!==t.keyCode)return this[e]+="Left"===i?-1:1,!0;n=this["findLineBoundary"+i](this[e])}return void 0!==typeof n&&this[e]!==n?(this[e]=n,!0):void 0},_moveLeft:function(t,e){return this._move(t,e,"Left")},_moveRight:function(t,e){return this._move(t,e,"Right")},moveCursorLeftWithoutShift:function(t){var e=!0;return this._selectionDirection="left",this.selectionEnd===this.selectionStart&&0!==this.selectionStart&&(e=this._moveLeft(t,"selectionStart")),this.selectionEnd=this.selectionStart,e},moveCursorLeftWithShift:function(t){return"right"===this._selectionDirection&&this.selectionStart!==this.selectionEnd?this._moveLeft(t,"selectionEnd"):0!==this.selectionStart?(this._selectionDirection="left",this._moveLeft(t,"selectionStart")):void 0},moveCursorRight:function(t){this.selectionStart>=this._text.length&&this.selectionEnd>=this._text.length||this._moveCursorLeftOrRight("Right",t)},_moveCursorLeftOrRight:function(t,e){var i="moveCursor"+t+"With";this._currentCursorOpacity=1,i+=e.shiftKey?"Shift":"outShift",this[i](e)&&(this.abortCursorAnimation(),this.initDelayedCursor(),this._fireSelectionChanged(),this._updateTextarea())},moveCursorRightWithShift:function(t){return"left"===this._selectionDirection&&this.selectionStart!==this.selectionEnd?this._moveRight(t,"selectionStart"):this.selectionEnd!==this._text.length?(this._selectionDirection="right",this._moveRight(t,"selectionEnd")):void 0},moveCursorRightWithoutShift:function(t){var e=!0;return this._selectionDirection="right",this.selectionStart===this.selectionEnd?(e=this._moveRight(t,"selectionStart"),this.selectionEnd=this.selectionStart):this.selectionStart=this.selectionEnd,e},removeChars:function(t,e){"undefined"==typeof e&&(e=t+1),this.removeStyleFromTo(t,e),this._text.splice(t,e-t),this.text=this._text.join(""),this.set("dirty",!0),this._shouldClearDimensionCache()&&(this.initDimensions(),this.setCoords()),this._removeExtraneousStyles()},insertChars:function(t,e,i,n){"undefined"==typeof n&&(n=i),n>i&&this.removeStyleFromTo(i,n);var r=fabric.util.string.graphemeSplit(t);this.insertNewStyleBlock(r,i,e),this._text=[].concat(this._text.slice(0,i),r,this._text.slice(n)),this.text=this._text.join(""),this.set("dirty",!0),this._shouldClearDimensionCache()&&(this.initDimensions(),this.setCoords()),this._removeExtraneousStyles()}});!function(){var t=fabric.util.toFixed,e=/ +/g;fabric.util.object.extend(fabric.Text.prototype,{_toSVG:function(){var t=this._getSVGLeftTopOffsets(),e=this._getSVGTextAndBg(t.textTop,t.textLeft);return this._wrapSVGTextAndBg(e)},toSVG:function(t){return this._createBaseSVGMarkup(this._toSVG(),{reviver:t,noStyle:!0,withShadow:!0})},_getSVGLeftTopOffsets:function(){return{textLeft:-this.width/2,textTop:-this.height/2,lineTop:this.getHeightOfLine(0)}},_wrapSVGTextAndBg:function(t){var e=!0,r=this.getSvgTextDecoration(this);return[t.textBgRects.join(""),' ",t.textSpans.join(""),"\n"]},_getSVGTextAndBg:function(t,e){var r,i=[],n=[],a=t;this._setSVGBg(n);for(var o=0,s=this._textLines.length;s>o;o++)r=this._getLineLeftOffset(o),(this.textBackgroundColor||this.styleHas("textBackgroundColor",o))&&this._setSVGTextLineBg(n,o,e+r,a),this._setSVGTextLineText(i,o,e+r,a),a+=this.getHeightOfLine(o);return{textSpans:i,textBgRects:n}},_createTextCharSpan:function(r,i,n,a){var o=r!==r.trim()||r.match(e),s=this.getSvgSpanStyles(i,o),c=s?'style="'+s+'"':"",l=i.deltaY,f="",u=fabric.Object.NUM_FRACTION_DIGITS;return l&&(f=' dy="'+t(l,u)+'" '),['",fabric.util.string.escapeXml(r),""].join("")},_setSVGTextLineText:function(t,e,r,i){var n,a,o,s,c,l=this.getHeightOfLine(e),f=-1!==this.textAlign.indexOf("justify"),u="",h=0,d=this._textLines[e];i+=l*(1-this._fontSizeFraction)/this.lineHeight;for(var p=0,g=d.length-1;g>=p;p++)c=p===g||this.charSpacing,u+=d[p],o=this.__charBounds[e][p],0===h?(r+=o.kernedWidth-o.width,h+=o.width):h+=o.kernedWidth,f&&!c&&this._reSpaceAndTab.test(d[p])&&(c=!0),c||(n=n||this.getCompleteStyleDeclaration(e,p),a=this.getCompleteStyleDeclaration(e,p+1),c=fabric.util.hasStyleChanged(n,a,!0)),c&&(s=this._getStyleDeclaration(e,p)||{},t.push(this._createTextCharSpan(u,s,r,i)),u="",n=a,r+=h,h=0)},_pushTextBgRect:function(e,r,i,n,a,o){var s=fabric.Object.NUM_FRACTION_DIGITS;e.push(" \n')},_setSVGTextLineBg:function(t,e,r,i){for(var n,a,o=this._textLines[e],s=this.getHeightOfLine(e)/this.lineHeight,c=0,l=0,f=this.getValueOfPropertyAt(e,0,"textBackgroundColor"),u=0,h=o.length;h>u;u++)n=this.__charBounds[e][u],a=this.getValueOfPropertyAt(e,u,"textBackgroundColor"),a!==f?(f&&this._pushTextBgRect(t,f,r+l,i,c,s),l=n.left,c=n.width,f=a):c+=n.kernedWidth;a&&this._pushTextBgRect(t,a,r+l,i,c,s)},_getFillAttributes:function(t){var e=t&&"string"==typeof t?new fabric.Color(t):"";return e&&e.getSource()&&1!==e.getAlpha()?'opacity="'+e.getAlpha()+'" fill="'+e.setAlpha(1).toRgb()+'"':'fill="'+t+'"'},_getSVGLineTopOffset:function(t){for(var e=0,r=0,i=0;t>i;i++)e+=this.getHeightOfLine(i);return r=this.getHeightOfLine(i),{lineTop:e,offset:(this._fontSizeMult-this._fontSizeFraction)*r/(this.lineHeight*this._fontSizeMult)}},getSvgStyles:function(t){var e=fabric.Object.prototype.getSvgStyles.call(this,t);return e+" white-space: pre;"}})}();!function(t){"use strict";var e=t.fabric||(t.fabric={});e.Textbox=e.util.createClass(e.IText,e.Observable,{type:"textbox",minWidth:20,dynamicMinWidth:2,__cachedLines:null,lockScalingFlip:!0,noScaleCache:!1,_dimensionAffectingProps:e.Text.prototype._dimensionAffectingProps.concat("width"),_wordJoiners:/[ \t\r]/,splitByGrapheme:!1,initDimensions:function(){this.__skipDimension||(this.isEditing&&this.initDelayedCursor(),this.clearContextTop(),this._clearCache(),this.dynamicMinWidth=0,this._styleMap=this._generateStyleMap(this._splitText()),this.dynamicMinWidth>this.width&&this._set("width",this.dynamicMinWidth),-1!==this.textAlign.indexOf("justify")&&this.enlargeSpaces(),this.height=this.calcTextHeight(),this.saveState({propertySet:"_dimensionAffectingProps"}))},_generateStyleMap:function(t){for(var e=0,r=0,n=0,i={},a=0;a0?(r=0,n++,e++):!this.splitByGrapheme&&this._reSpaceAndTab.test(t.graphemeText[n])&&a>0&&(r++,n++),i[a]={line:e,offset:r},n+=t.graphemeLines[a].length,r+=t.graphemeLines[a].length;return i},styleHas:function(t,r){if(this._styleMap&&!this.isWrapping){var n=this._styleMap[r];n&&(r=n.line)}return e.Text.prototype.styleHas.call(this,t,r)},isEmptyStyles:function(t){if(!this.styles)return!0;var e,r,n=0,i=t+1,a=!1,o=this._styleMap[t],c=this._styleMap[t+1];o&&(t=o.line,n=o.offset),c&&(i=c.line,a=i===t,e=c.offset),r="undefined"==typeof t?this.styles:{line:this.styles[t]};for(var s in r)for(var l in r[s])if(l>=n&&(!a||e>l))for(var f in r[s][l])return!1;return!0},_getStyleDeclaration:function(t,e){if(this._styleMap&&!this.isWrapping){var r=this._styleMap[t];if(!r)return null;t=r.line,e=r.offset+e}return this.callSuper("_getStyleDeclaration",t,e)},_setStyleDeclaration:function(t,e,r){var n=this._styleMap[t];t=n.line,e=n.offset+e,this.styles[t][e]=r},_deleteStyleDeclaration:function(t,e){var r=this._styleMap[t];t=r.line,e=r.offset+e,delete this.styles[t][e]},_getLineStyle:function(t){var e=this._styleMap[t];return!!this.styles[e.line]},_setLineStyle:function(t){var e=this._styleMap[t];this.styles[e.line]={}},_wrapText:function(t,e){var r,n=[];for(this.isWrapping=!0,r=0;ro;o++){var s=this._getGraphemeBox(t[o],e,o+r,n,a);i+=s.kernedWidth,n=t[o]}return i},_wrapLine:function(t,r,n,i){var a=0,o=this.splitByGrapheme,c=[],s=[],l=o?e.util.string.graphemeSplit(t):t.split(this._wordJoiners),f="",u=0,h=o?"":" ",d=0,b=0,p=0,m=!0,y=this._getWidthOfCharSpacing(),i=i||0;0===l.length&&l.push([]),n-=i;for(var g=0;gn&&!m?(c.push(s),s=[],a=d,m=!0):a+=y,m||o||s.push(h),s=s.concat(f),b=o?0:this._measureWord([h],r,u),u++,m=!1,d>p&&(p=d);return g&&c.push(s),p+i>this.dynamicMinWidth&&(this.dynamicMinWidth=p-y+i),c},isEndOfWrapping:function(t){return this._styleMap[t+1]?this._styleMap[t+1].line!==this._styleMap[t].line?!0:!1:!0},missingNewlineOffset:function(t){return this.splitByGrapheme?this.isEndOfWrapping(t)?1:0:1},_splitTextIntoLines:function(t){for(var r=e.Text.prototype._splitTextIntoLines.call(this,t),n=this._wrapText(r.lines,this.width),i=new Array(n.length),a=0;a", + "hl.simple.post": "", + "hl.snippets": 30, + "hl.fragsize": 100 } # Specify which field to use in the tag cloud on the homepage. diff --git a/app/controllers/flipflop/strategies_controller_decorator.rb b/app/controllers/flipflop/strategies_controller_decorator.rb new file mode 100644 index 000000000..7c508a5bf --- /dev/null +++ b/app/controllers/flipflop/strategies_controller_decorator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# OVERRIDE Flipflop v2.7.1 to allow for custom `Action` labels + +module Flipflop + module StrategiesControllerDecorator + def enable? + values = StrategiesController::ENABLE_VALUES | ADDITIONAL_ENABLE_VALUES + values.include?(params[:commit]) + end + + ADDITIONAL_ENABLE_VALUES = FeaturesHelper::FEATURE_ACTION_LABELS.map { |_, v| v[:on] }.to_set.freeze + end +end + +Flipflop::StrategiesController.prepend(Flipflop::StrategiesControllerDecorator) diff --git a/app/controllers/hyrax/admin/appearances_controller.rb b/app/controllers/hyrax/admin/appearances_controller.rb index b37308ebc..41fd636e0 100644 --- a/app/controllers/hyrax/admin/appearances_controller.rb +++ b/app/controllers/hyrax/admin/appearances_controller.rb @@ -26,7 +26,10 @@ def show end def update - form_class.new(update_params).update! + form = form_class.new(update_params) + form.banner_image = update_params[:banner_image] if update_params[:banner_image].present? + + form.update! if update_params['default_collection_image'] # Reindex all Collections and AdminSets to apply new default collection image diff --git a/app/controllers/hyrax/homepage_controller.rb b/app/controllers/hyrax/homepage_controller.rb index a38eda845..17e67d07b 100644 --- a/app/controllers/hyrax/homepage_controller.rb +++ b/app/controllers/hyrax/homepage_controller.rb @@ -33,17 +33,13 @@ def search_builder_class # override hyrax v2.9.0 added @home_text - Adding Themes def index - @presenter = presenter_class.new(current_ability, collections) @featured_researcher = ContentBlock.for(:researcher) - @marketing_text = ContentBlock.for(:marketing) @home_text = ContentBlock.for(:home_text) @featured_work_list = FeaturedWorkList.new # OVERRIDE here to add featured collection list @featured_collection_list = FeaturedCollectionList.new - @announcement_text = ContentBlock.for(:announcement) + load_shared_info recent - ir_counts if home_page_theme == 'institutional_repository' - # override hyrax v2.9.0 added for facets on homepage - Adding Themes (@response, @document_list) = search_results(params) @@ -65,11 +61,7 @@ def index def browserconfig; end def all_collections - @presenter = presenter_class.new(current_ability, collections) - @marketing_text = ContentBlock.for(:marketing) - @announcement_text = ContentBlock.for(:announcement) - @collections = collections(rows: 100_000) - ir_counts if home_page_theme == 'institutional_repository' + load_shared_info end # Added from Blacklight 6.23.0 to change url for facets on home page @@ -85,6 +77,26 @@ def search_action_url(options = {}) private + # shared methods for index and all_collections routes + def load_shared_info + @presenter = presenter_class.new(current_ability, collections) + @marketing_text = ContentBlock.for(:marketing) + @announcement_text = ContentBlock.for(:announcement) + @collections = collections(rows: 100_000) + # rubocop:disable Style/GuardClause + # TODO: Why not make these helper methods? As is we rely on a theme to set instance + # variables. Which is fragile. + if home_page_theme == 'institutional_repository' + ir_counts + @top_level_collections ||= load_top_level_collections(@collections) + end + # rubocop:enable Style/GuardClause + end + + def load_top_level_collections(colls) + colls.select { |c| c['member_of_collection_ids_ssim'].nil? } + end + # Return 6 collections def collections(rows: 6) builder = Hyrax::CollectionSearchBuilder.new(self) @@ -120,7 +132,6 @@ def inject_theme_views prepend_view_path(home_theme_view_path) yield # rubocop:disable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses - # Do NOT change this line. This is calling the Rails view_paths=(paths) method and not a variable assignment. view_paths=(original_paths) # rubocop:enable Lint/UselessAssignment, Layout/SpaceAroundOperators, Style/RedundantParentheses else diff --git a/app/forms/hyrax/forms/admin/appearance.rb b/app/forms/hyrax/forms/admin/appearance.rb index 7f20affae..599297f99 100644 --- a/app/forms/hyrax/forms/admin/appearance.rb +++ b/app/forms/hyrax/forms/admin/appearance.rb @@ -26,6 +26,7 @@ class Appearance 'header_and_footer_background_color' => '#3c3c3c', 'header_and_footer_text_color' => '#dcdcdc', 'navbar_background_color' => '#000000', + 'navbar_link_background_color' => '#375f8c', 'navbar_link_background_hover_color' => '#ffffff', 'navbar_link_text_color' => '#eeeeee', 'navbar_link_text_hover_color' => '#eeeeee', @@ -80,6 +81,8 @@ def persisted? true end + delegate :banner_image=, to: :site + # The alt text for the logo image def logo_image_text block_for('logo_image_text') @@ -138,6 +141,14 @@ def navbar_background_color_active darken_color(navbar_background_color, 0.35) end + def navbar_link_background_color + block_for('navbar_link_background_color') + end + + def navbar_link_background_color_active + darken_color(navbar_link_background_color, 0.35) + end + def navbar_link_background_hover_color block_for('navbar_link_background_hover_color') end @@ -381,6 +392,7 @@ def self.customization_params facet_panel_background_color facet_panel_text_color navbar_background_color + navbar_link_background_color navbar_link_background_hover_color navbar_link_text_color navbar_link_text_hover_color diff --git a/app/forms/hyrax/generic_work_form.rb b/app/forms/hyrax/generic_work_form.rb index 132f11bd8..de0630d1a 100644 --- a/app/forms/hyrax/generic_work_form.rb +++ b/app/forms/hyrax/generic_work_form.rb @@ -7,6 +7,8 @@ class GenericWorkForm < Hyrax::Forms::WorkForm include Hyrax::FormTerms self.model_class = ::GenericWork include HydraEditor::Form::Permissions - self.terms += %i[resource_type] + include PdfFormBehavior + + self.terms += %i[resource_type bibliographic_citation] end end diff --git a/app/forms/hyrax/image_form.rb b/app/forms/hyrax/image_form.rb index 3d3e09b53..f2b27cd25 100644 --- a/app/forms/hyrax/image_form.rb +++ b/app/forms/hyrax/image_form.rb @@ -6,6 +6,8 @@ module Hyrax class ImageForm < Hyrax::Forms::WorkForm include Hyrax::FormTerms self.model_class = ::Image - self.terms += %i[resource_type extent] + include PdfFormBehavior + + self.terms += %i[resource_type extent bibliographic_citation] end end diff --git a/app/forms/hyrax/pdf_form_behavior.rb b/app/forms/hyrax/pdf_form_behavior.rb new file mode 100644 index 000000000..cd909a56a --- /dev/null +++ b/app/forms/hyrax/pdf_form_behavior.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Hyrax + module PdfFormBehavior + extend ActiveSupport::Concern + + included do + class_attribute :hidden_terms + + self.terms += %i[show_pdf_viewer show_pdf_download_button] + self.hidden_terms = %i[show_pdf_viewer show_pdf_download_button] + end + + def hidden?(key) + hidden_terms.include? key.to_sym + end + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e27eee4ae..2b4d5d3b4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,11 +1,17 @@ # frozen_string_literal: true module ApplicationHelper + # Yep, we're ignoring the advice; because the translations are safe as is the markdown converter. + # rubocop:disable Rails/OutputSafety include ::HyraxHelper include Hyrax::OverrideHelperBehavior include GroupNavigationHelper include SharedSearchHelper + def label_for(term:, record_class: nil) + locale_for(type: 'labels', term: term, record_class: record_class) + end + def hint_for(term:, record_class: nil) hint = locale_for(type: 'hints', term: term, record_class: record_class) @@ -16,8 +22,8 @@ def locale_for(type:, term:, record_class:) @term = term.to_s @record_class = record_class.to_s.downcase work_or_collection = @record_class == 'collection' ? 'collection' : 'defaults' - default_locale = t("simple_form.#{type}.#{work_or_collection}.#{@term}") - locale = t("hyrax.#{@record_class}.#{type}.#{@term}") + default_locale = t("simple_form.#{type}.#{work_or_collection}.#{@term}").html_safe + locale = t("hyrax.#{@record_class}.#{type}.#{@term}").html_safe return default_locale if missing_translation(locale) @@ -27,4 +33,15 @@ def locale_for(type:, term:, record_class:) def missing_translation(value) value.include?('translation missing') end + + def markdown(text) + options = %i[ + hard_wrap autolink no_intra_emphasis tables fenced_code_blocks + disable_indented_code_blocks strikethrough lax_spacing space_after_headers + quote footnotes highlight underline + ] + text ||= "" + Markdown.new(text, *options).to_html.html_safe + end + # rubocop:enable Rails/OutputSafety end diff --git a/app/helpers/blacklight/advanced_search_helper.rb b/app/helpers/blacklight/advanced_search_helper.rb new file mode 100644 index 000000000..2e848270b --- /dev/null +++ b/app/helpers/blacklight/advanced_search_helper.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Blacklight + # Helpers related to the advanced search functionality of Blacklight. + module AdvancedSearchHelper + # Retrieves the first six search fields from a given collection. + # + # @param fields [Array] collection of search fields + # @return [Array] a subset of the input collection containing the first six fields + def primary_search_fields_for(fields) + fields.each_with_index.partition { |_, idx| idx < 6 }.first.map(&:first) + end + + # Retrieves all search fields from a given collection except the first six. + # + # @param fields [Array] collection of search fields + # @return [Array] a subset of the input collection excluding the first six fields + def secondary_search_fields_for(fields) + fields.each_with_index.partition { |_, idx| idx < 6 }.last.map(&:first) + end + + # Determines if the provided key represents a local authority. + # + # @param key [String] the key to be checked + # @return [Boolean] true if the key or its pluralized form is found in the local authorities list; false otherwise + def local_authority?(key) + local_qa_names = Qa::Authorities::Local.names + local_qa_names.include?(key.pluralize) || local_qa_names.include?(key) + end + + # Gets the options for a QA select based on a given key. + # + # @param key [String] the key used to fetch the service and retrieve options + # @return [Array, nil] the options available for the select, or nil if the service does not provide any options + def options_for_qa_select(key) + service = fetch_service_for(key) + service.try(:select_all_options) || service.try(:select_options) || service.new.select_all_options + end + + private + + # Fetches the service for a given key. + # + # @param key [String] the key used to determine the service name + # @return [Class, nil] the service class based on the key, or nil if it does not exist + def fetch_service_for(key) + "Hyrax::#{key.camelize}Service".safe_constantize || "Hyrax::#{key.pluralize.camelize}Service".safe_constantize + end + end +end diff --git a/app/helpers/features_helper.rb b/app/helpers/features_helper.rb new file mode 100644 index 000000000..6d630fd04 --- /dev/null +++ b/app/helpers/features_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module FeaturesHelper + def status_for(feature) + status = @feature_set.status(feature) + label = @feature_set.status(feature) == :enabled ? :on : :off + FEATURE_ACTION_LABELS.fetch(feature.name.to_sym, label => status)[label] + end + + def on(feature) + FEATURE_ACTION_LABELS[feature]&.[](:on) || 'on' + end + + def off(feature) + FEATURE_ACTION_LABELS[feature]&.[](:off) || 'off' + end + + FEATURE_ACTION_LABELS = { + default_pdf_viewer: { on: 'PDF.js', off: 'UV' } + }.freeze +end diff --git a/app/helpers/hyku_helper.rb b/app/helpers/hyku_helper.rb index c33acd741..dd76ef205 100644 --- a/app/helpers/hyku_helper.rb +++ b/app/helpers/hyku_helper.rb @@ -19,4 +19,10 @@ def admin_host? def admin_only_tenant_creation? ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYKU_ADMIN_ONLY_TENANT_CREATION', false)) end + + def parent_path(parent_doc) + model = parent_doc['has_model_ssim'].first + path = "hyrax_#{model.underscore}_path" + main_app.send(path, parent_doc.id) + end end diff --git a/app/helpers/iiif_print_helper.rb b/app/helpers/iiif_print_helper.rb new file mode 100644 index 000000000..0c1859cfa --- /dev/null +++ b/app/helpers/iiif_print_helper.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module IiifPrintHelper + include IiifPrint::IiifPrintHelperBehavior +end diff --git a/app/helpers/pdf_js_helper.rb b/app/helpers/pdf_js_helper.rb new file mode 100644 index 000000000..8ba877fa9 --- /dev/null +++ b/app/helpers/pdf_js_helper.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module PdfJsHelper + def pdf_js_url(path) + "/pdf.js/viewer.html?file=#{path}##{query_param}" + end + + def pdf_file_set_presenter(presenter) + # currently only supports one pdf per work, falls back to the first pdf file set in ordered members + representative_presenter(presenter) || presenter.file_set_presenters.select(&:pdf?).first + end + + def representative_presenter(presenter) + presenter.file_set_presenters.find { |file_set_presenter| file_set_presenter.id == presenter.representative_id } + end + + def query_param + return unless params[:q] + + "search=#{params[:q]}&phrase=true" + end + + def render_show_pdf_behavior_checkbox? + return unless Flipflop.default_pdf_viewer? + return if params[:id].nil? + + doc = SolrDocument.find params[:id] + + presenter = @_controller.show_presenter.new(doc, current_ability) + presenter.file_set_presenters.any?(&:pdf?) + end +end diff --git a/app/helpers/shared_search_helper.rb b/app/helpers/shared_search_helper.rb index ef419f6cb..ee213710b 100644 --- a/app/helpers/shared_search_helper.rb +++ b/app/helpers/shared_search_helper.rb @@ -17,7 +17,10 @@ def generate_work_url(model, request) id = model["id"] end request_params = %i[protocol host port].map { |method| ["request_#{method}".to_sym, request.send(method)] }.to_h - get_url(id: id, request: request_params, account_cname: account_cname, has_model: has_model) + url = get_url(id: id, request: request_params, account_cname: account_cname, has_model: has_model) + + # pass search query params to work show page + params[:q].present? ? "#{url}?q=#{params[:q]}" : url end private diff --git a/app/indexers/app_indexer.rb b/app/indexers/app_indexer.rb index 0567be937..1da15015f 100644 --- a/app/indexers/app_indexer.rb +++ b/app/indexers/app_indexer.rb @@ -12,7 +12,26 @@ class AppIndexer < Hyrax::WorkIndexer # Uncomment this block if you want to add custom indexing behavior: def generate_solr_document super.tap do |solr_doc| - solr_doc["account_cname_tesim"] = Site.instance&.account&.cname + solr_doc['account_cname_tesim'] = Site.instance&.account&.cname + solr_doc['bulkrax_identifier_tesim'] = object.bulkrax_identifier if object.respond_to?(:bulkrax_identifier) + solr_doc['all_text_tsimv'] = full_text(object.file_sets.first&.id) + add_date(solr_doc) end end + + def full_text(file_set_id) + return if !Flipflop.default_pdf_viewer? || file_set_id.blank? + + SolrDocument.find(file_set_id)['all_text_tsimv'] + end + + def add_date(solr_doc) + # The allowed date formats are either YYYY, YYYY-MM, or YYYY-MM-DD + # the date must be formatted as a 4 digit year in order to be sorted. + valid_date_formats = /\A(\d{4})(?:-\d{2}(?:-\d{2})?)?\z/ + date_string = solr_doc['date_created_tesim']&.first + year = date_string&.match(valid_date_formats)&.captures&.first + solr_doc['date_tesi'] = year if year + solr_doc['date_ssi'] = year if year + end end diff --git a/app/indexers/generic_work_indexer.rb b/app/indexers/generic_work_indexer.rb index f0f08119e..4466bc78e 100644 --- a/app/indexers/generic_work_indexer.rb +++ b/app/indexers/generic_work_indexer.rb @@ -5,8 +5,8 @@ class GenericWorkIndexer < AppIndexer # Uncomment this block if you want to add custom indexing behavior: # def generate_solr_document - # super.tap do |solr_doc| - # solr_doc['my_custom_field_ssim'] = object.my_custom_property - # end + # super.tap do |solr_doc| + # solr_doc['admin_note_tesim'] = object.admin_note + # end # end end diff --git a/app/indexers/hyrax/file_set_indexer_decorator.rb b/app/indexers/hyrax/file_set_indexer_decorator.rb new file mode 100644 index 000000000..0fdd59eeb --- /dev/null +++ b/app/indexers/hyrax/file_set_indexer_decorator.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# OVERRIDE Hyrax 3.6.0 to add PDF text to solr document when using the default PDF viewer (PDF.js) + +module Hyrax + module FileSetIndexerDecorator + def generate_solr_document + return super unless Flipflop.default_pdf_viewer? + + super.tap do |solr_doc| + solr_doc['all_text_timv'] = solr_doc['all_text_tsimv'] = pdf_text + end + end + + private + + def pdf_text + return unless object.pdf? + return unless object.original_file&.content.is_a? String + + begin + text = IO.popen(['pdftotext', '-', '-'], 'r+b') do |pdftotext| + pdftotext.write(object.original_file.content) + pdftotext.close_write + pdftotext.read + end + + text.tr("\n", ' ') + .squeeze(' ') + .encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') # remove non-UTF-8 characters + rescue Errno::ENOENT => e + raise e unless e.message.include?("No such file or directory - pdftotext") + Rails.logger.warn("`pdfinfo' is not installed; unable to extract text from the PDF's content") + end + end + end +end + +Hyrax::FileSetIndexer.prepend(Hyrax::FileSetIndexerDecorator) diff --git a/app/jobs/reindex_works_job.rb b/app/jobs/reindex_works_job.rb index 56adcf2d0..5d9c50cfc 100644 --- a/app/jobs/reindex_works_job.rb +++ b/app/jobs/reindex_works_job.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true class ReindexWorksJob < ApplicationJob - def perform - Site.instance.available_works.each do |work_type| - work_type.constantize.find_each do |work| - ReindexItemJob.perform_later(work) + def perform(work = nil) + if work.present? + work.update_index + else + Hyrax.config.registered_curation_concern_types.each do |work_type| + work_type.constantize.find_each do |w| + ReindexItemJob.perform_later(w) + end end end end diff --git a/app/models/concerns/pdf_behavior.rb b/app/models/concerns/pdf_behavior.rb new file mode 100644 index 000000000..af88f8189 --- /dev/null +++ b/app/models/concerns/pdf_behavior.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module RDF + class CustomShowPdfViewerTerm < Vocabulary('http://id.loc.gov/vocabulary/identifiers/') + property 'show_pdf_viewer' + end + + class CustomShowPdfDownloadButtonTerm < Vocabulary('http://id.loc.gov/vocabulary/identifiers/') + property 'show_pdf_download_button' + end +end + +module PdfBehavior + extend ActiveSupport::Concern + + included do + property :show_pdf_viewer, predicate: RDF::CustomShowPdfViewerTerm.show_pdf_viewer, multiple: false do |index| + index.as :stored_searchable + end + + # rubocop:disable Metrics/LineLength + property :show_pdf_download_button, predicate: RDF::CustomShowPdfDownloadButtonTerm.show_pdf_download_button, multiple: false do |index| + index.as :stored_searchable + end + # rubocop:enable Metrics/LineLength + + after_initialize :set_default_show_pdf_viewer, :set_default_show_pdf_download_button + end + + private + + # This is here so that the checkbox is checked by default + def set_default_show_pdf_viewer + self.show_pdf_viewer ||= '1' + end + + def set_default_show_pdf_download_button + self.show_pdf_download_button ||= '1' + end +end diff --git a/app/models/generic_work.rb b/app/models/generic_work.rb index f69aae6ab..ed7d2b53a 100644 --- a/app/models/generic_work.rb +++ b/app/models/generic_work.rb @@ -2,10 +2,13 @@ class GenericWork < ActiveFedora::Base include ::Hyrax::WorkBehavior + include PdfBehavior include ::Hyrax::BasicMetadata + if ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYKU_IIIF_PRINT', false)) include IiifPrint.model_configuration( - pdf_split_child_model: self + pdf_split_child_model: GenericWork, + pdf_splitter_service: IiifPrint::TenantConfig::PdfSplitter ) end diff --git a/app/models/image.rb b/app/models/image.rb index 51afe7599..3d6e7bf2d 100644 --- a/app/models/image.rb +++ b/app/models/image.rb @@ -4,9 +4,12 @@ # `rails generate hyrax:work Image` class Image < ActiveFedora::Base include ::Hyrax::WorkBehavior + include PdfBehavior + if ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYKU_IIIF_PRINT', false)) include IiifPrint.model_configuration( - pdf_split_child_model: self + pdf_split_child_model: GenericWork, + pdf_splitter_service: IiifPrint::TenantConfig::PdfSplitter ) end diff --git a/app/models/solr_document.rb b/app/models/solr_document.rb index 2290e6986..d14c1840b 100644 --- a/app/models/solr_document.rb +++ b/app/models/solr_document.rb @@ -45,4 +45,24 @@ class SolrDocument title: 'title_tesim', type: 'human_readable_type_tesim' ) + + def show_pdf_viewer + self['show_pdf_viewer_tesim'] + end + + def show_pdf_download_button + self['show_pdf_download_button_tesim'] + end + + # @return [Array] a list of solr documents in no particular order + def load_parent_docs + query("member_ids_ssim: #{id}", rows: 1000) + .map { |res| ::SolrDocument.new(res) } + end + + # Query solr using POST so that the query doesn't get too large for a URI + def query(query, **opts) + result = Hyrax::SolrService.post(query, **opts) + result.fetch('response').fetch('docs', []) + end end diff --git a/app/presenters/hyku/work_show_presenter.rb b/app/presenters/hyku/work_show_presenter.rb index 12427bc41..2e9f38d99 100644 --- a/app/presenters/hyku/work_show_presenter.rb +++ b/app/presenters/hyku/work_show_presenter.rb @@ -8,9 +8,17 @@ class WorkShowPresenter < Hyrax::WorkShowPresenter # Hyrax::MemberPresenterFactory.file_presenter_class = Hyrax::FileSetPresenter # Adds behaviors for hyrax-iiif_av plugin. include Hyrax::IiifAv::DisplaysIiifAv + + ## + # NOTE: IIIF Print prepends a IiifPrint::WorkShowPresenterDecorator to Hyrax::WorkShowPresenter + # However, with the above `include Hyrax::IiifAv::DisplaysIiifAv` we obliterate that logic. So + # we need to re-introduce that logic. + prepend IiifPrint::TenantConfig::WorkShowPresenterDecorator + Hyrax::MemberPresenterFactory.file_presenter_class = Hyrax::IiifAv::IiifFileSetPresenter - delegate :title_or_label, :extent, to: :solr_document + delegate :title_or_label, :extent, :source, :bibliographic_citation, :date, + :show_pdf_viewer, :show_pdf_download_button, to: :solr_document # OVERRIDE Hyrax v2.9.0 here to make featured collections work delegate :collection_presenters, to: :member_presenter_factory @@ -30,6 +38,18 @@ def isbns isbns&.flatten&.compact end + # OVERRIDE FILE from Hyrax v2.9.0 + # @return [String] title update for GenericWork + Hyrax::WorkShowPresenter.class_eval do + def page_title + if human_readable_type == "Generic Work" + "#{title.first} | ID: #{id} | #{I18n.t('hyrax.product_name')}" + else + "#{human_readable_type} | #{title.first} | ID: #{id} | #{I18n.t('hyrax.product_name')}" + end + end + end + # OVERRIDE here for featured collection methods # Begin Featured Collections Methods def collection_featurable? @@ -57,26 +77,46 @@ def user_can_feature_collection? end # End Featured Collections Methods - # @return [Boolean] render a IIIF viewer - def iiif_viewer? - Hyrax.config.iiif_image_server? && - representative_id.present? && - representative_presenter.present? && - iiif_media? && - members_include_viewable? + def show_pdf_viewer? + return unless Flipflop.default_pdf_viewer? + return unless show_pdf_viewer + return unless file_set_presenters.any?(&:pdf?) + + show_pdf_viewer.first.to_i.positive? end - private + def show_pdf_download_button? + return unless file_set_presenters.any?(&:pdf?) + return unless show_pdf_download_button - def iiif_media?(presenter: representative_presenter) - presenter.image? || presenter.video? || presenter.audio? || presenter.pdf? - end + show_pdf_download_button.first.to_i.positive? + end - def members_include_viewable? - file_set_presenters.any? do |presenter| - iiif_media?(presenter: presenter) && current_ability.can?(:read, presenter.id) + def viewer? + iiif_viewer? || video_embed_viewer? || show_pdf_viewer? + end + + def parent_works(current_user = nil) + @parent_works ||= begin + docs = solr_document.load_parent_docs + + if current_user + docs.select { |doc| current_user.ability.can?(:read, doc) } + else + docs.select(&:public?) end end + end + + def video_embed_viewer? + extract_video_embed_presence + end + + private + + def extract_video_embed_presence + solr_document[:video_embed_tesim]&.first&.present? + end def extract_from_identifier(rgx) if solr_document['identifier_tesim'].present? diff --git a/app/presenters/hyrax/generic_work_presenter.rb b/app/presenters/hyrax/generic_work_presenter.rb index 162b6ffe1..7ddaa3264 100644 --- a/app/presenters/hyrax/generic_work_presenter.rb +++ b/app/presenters/hyrax/generic_work_presenter.rb @@ -4,5 +4,6 @@ # `rails generate hyrax:work GenericWork` module Hyrax class GenericWorkPresenter < Hyku::WorkShowPresenter + delegate :abstract, to: :solr_document end end diff --git a/app/services/iiif_print/tenant_config.rb b/app/services/iiif_print/tenant_config.rb new file mode 100644 index 000000000..96bbaf95c --- /dev/null +++ b/app/services/iiif_print/tenant_config.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +# rubocop:disable Metrics/LineLength +module IiifPrint + ## + # This module encapsulates the logic for whether or not we'll use the IIIF Print services for the + # current tenant/account. The IIIF Print services does the following: + # + # - Skipping IIIF Print based derivative generation + # - Skipping PDF Splitting + # - Ignoring showing PDFs in the UV + # + # @note I am specifically isolating as much of this code into one module as possible, so that it + # it is hopefully easier to understand the configuration requirements and scope to this + # change. At some point, this might make sense to bring into IIIF Print directly. + # + # @see https://github.com/scientist-softserv/palni-palci/issues/656 palni-palci#656 + # @see https://github.com/scientist-softserv/palni-palci/issues/657 palni-palci#657 + # @see https://github.com/scientist-softserv/palni-palci/issues/658 palni-palci#658 + # @see https://github.com/scientist-softserv/palni-palci/issues/659 palni-palci#659 + module TenantConfig + ## + # When we were not planning on calling the underlying IiifPrint service but did due to some kind + # of faulty programming logic. + # + # @note This is raised as a guard to say "Hey, you thought you weren't using IIIF Print but your + # code's logic paths say otherwise." + class LeakyAbstractionError < StandardError + def initialize(klass:, method_name:) + super("Called #{klass}##{method_name} when we had said that #{klass} was not valid because we weren't using IIIF Print") + end + end + + ## + # If the default PDF viewer (PDF.js) is enabled, this method returns false, + # meaning the application should not use IIIF Print. If the default viewer is + # disabled, this method returns true, meaning the application should use IIIF Print. + def self.use_iiif_print? + !::Flipflop.default_pdf_viewer? + end + + ## + # This class implements the interface of the Hyrax::DerivativeService. It is responsible for + # negotiating whether or not the DerivativeService is "on" for the current tenant. + # + # @see https://github.com/samvera/hyrax/blob/08ef6c9a4fac489972eea9be53403e173f4ffb29/app/services/hyrax/derivative_service.rb Hyrax::DerivativeService + class DerivativeService + ## + # This allows you to specify the IIIF derivative service to use when the tenant has chosen to + # use IIIF Print for processing PDFs. + # + # If you are using the DerivativeRodeo, you'd specify something else. + class_attribute :iiif_service_class, default: ::IiifPrint::PluggableDerivativeService + + def initialize(file_set) + @file_set = file_set + end + + delegate :use_iiif_print?, to: TenantConfig + + def valid? + return false unless use_iiif_print? + + iiif_print_service_instance.valid? + end + + %i[create_derivatives cleanup_derivatives].each do |method_name| + define_method(method_name) do |*args| + raise LeakyAbstractionError.new(klass: self.class, method_name: method_name) unless use_iiif_print? + + iiif_print_service_instance.public_send(method_name, *args) + end + end + + ## + # @api private + # + # @note Public to ease testing. + def iiif_print_service_instance + @iiif_print_service_instance ||= iiif_service_class.new(@file_set) + end + end + + ## + # This is the pdf_splitter_service that will be used. If the tenant does not allow PDF splitting + # we will return an empty array. + # + # @example + # + # class MyWork + # include IiifPrint.model_configuration( + # pdf_split_child_model: Attachment, + # pdf_splitter_service: IiifPrint::TenantConfig::PdfSplitter, + # derivative_service_plugins: [ IiifPrint::TextExtractionDerivativeService ]) + # end + # + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/lib/iiif_print.rb#L86-L138 Documentation for configuring + # @see https://github.com/scientist-softserv/adventist-dl/blob/d7676bdac2c672f09b28086d7145b68306978950/app/models/image.rb#L14-L20 Example implementation + module PdfSplitter + mattr_accessor :iiif_print_splitter + self.iiif_print_splitter = ::IiifPrint::SplitPdfs::PagesToJpgsSplitter + + ## + # @api public + def self.call(*args) + return [] unless TenantConfig.use_iiif_print? + + iiif_print_splitter.call(*args) + end + end + + ## + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/lib/iiif_print/split_pdfs/child_work_creation_from_pdf_service.rb#L10-L46 Interface of FileSetActor#service + module SkipSplittingPdfService + ## + # @return [Symbol] Always :tenant_does_not_split_pdfs + def self.conditionally_enqueue(*_args) + :tenant_does_not_split_pdfs + end + end + + ## + # This decorator should ensure that we don't call model configured :pdf_splitter_service as + # documented in {TenantConfig::PdfSplitter} and the IIIF Print gem. It avoids the potentially + # expensive conditionally enqueue logic of the super class. + # + # Why not make an `app/actors/hyrax/actors/file_set_actor_decorator.rb`? It would be lost in that + # it is decorating the decoration of the IIIF Print gem. Beside, in bringing this here, we have + # a relatively singular place for all of the configurations. + module FileSetActorDecorator + ## + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/app/actors/iiif_print/actors/file_set_actor_decorator.rb#L33-L35 Method we're overriding + def service + return TenantConfig::SkipSplittingPdfService unless TenantConfig.use_iiif_print? + + super + end + end + + ## + # OVERRIDE IiifPrint::WorkShowPresenterDecorator + # OVERRIDE Hyrax::WorkShowPresenter + # + # In IiifPrint we overrided #members_include_viewable_image? to query for both file sets and + # child works. (Child works being the pages split off of a PDF) + # + # In Hyrax::WorkShowPresenter we're only looking at the underlying file_sets. But IiifPrint + # needs to look at multiple places. + module WorkShowPresenterDecorator + ## + # @return [Array] predicate methods (e.g. ending in "?") that reflect the types + # of files we want to consider for showing in the IIIF Viewer. + def iiif_media_predicates + if TenantConfig.use_iiif_print? + %i[image? audio? video? pdf?] + else + %i[image? audio? video?] + end + end + + def iiif_media?(presenter: representative_presenter) + iiif_media_predicates.any? { |predicate| presenter.try(predicate) || presenter.try(:solr_document).try(predicate) } + end + + ## + # @return [Boolean] render a IIIF viewer + # + # OVERRIDE Hyrax::WorkShowPresenter; this override introduces behavior to handle over-rides. + def iiif_viewer? + Hyrax.config.iiif_image_server? && + representative_id.present? && + representative_presenter.present? && + iiif_media? && + members_include_iiif_viewable? + end + + def members_include_iiif_viewable? + iiif_presentable_member_presenters.any? do |presenter| + iiif_media?(presenter: presenter) && current_ability.can?(:read, presenter.id) + end + end + + ## + # @return [Array] An array of presenter objects + # + # In a non-IIIF Print using scenario, we use the file_set_presenters value; that is for + # objects that are very specifically file_sets. + # + # In a IIIF Print using scenario, we use the ill-named 'file_set_ids_ssim', because a + # long-standing decision is that this field will have both file_set IDs and child work IDs. + def iiif_presentable_member_presenters + if TenantConfig.use_iiif_print? + presentable_member_ids = Array.wrap(solr_document.try(:file_set_ids) || solr_document.try(:[], 'file_set_ids_ssim')) + member_presenters_for(presentable_member_ids) + else + file_set_presenters + end + end + end + end +end +# rubocop:enable Metrics/LineLength diff --git a/app/views/advanced/_advanced_search_fields.html.erb b/app/views/advanced/_advanced_search_fields.html.erb new file mode 100644 index 000000000..2f98780be --- /dev/null +++ b/app/views/advanced/_advanced_search_fields.html.erb @@ -0,0 +1,35 @@ +<% + # OVERRIDE blacklight_advanced_search v6.4.1 to split the first six fields + # See: CatalogController#search_fields_without_customization= + # See: Blacklight::BlacklightHelperBehavior#primary_search_fields, #secondary_search_fields + # Also using Bootstrap 3 classes to make the form resemble the Hyrax form behaviors +%> +<%- primary_search_fields_for(search_fields_for_advanced_search).each do |key, field_def| -%> +
+ <%= label_tag key, "#{field_def.label}", :class => "col-sm-3 control-label" %> +
+ <% if local_authority?(key) %> + <%= render 'advanced_search_fields_qa', key: key %> + <% else %> + <%= text_field_tag key, label_tag_default_for(key), :class => 'form-control' %> + <% end %> +
+
+<%- end -%> + + + + diff --git a/app/views/advanced/_advanced_search_fields_qa.html.erb b/app/views/advanced/_advanced_search_fields_qa.html.erb new file mode 100644 index 000000000..24e8ff7cd --- /dev/null +++ b/app/views/advanced/_advanced_search_fields_qa.html.erb @@ -0,0 +1,4 @@ +<%= select_tag key, + options_for_select(options_for_qa_select(key)), + class: 'form-control', + include_blank: true %> diff --git a/app/views/hyrax/admin/appearances/_banner_image_form.html.erb b/app/views/hyrax/admin/appearances/_banner_image_form.html.erb index c92d3b4c0..6fad2ad6d 100644 --- a/app/views/hyrax/admin/appearances/_banner_image_form.html.erb +++ b/app/views/hyrax/admin/appearances/_banner_image_form.html.erb @@ -2,18 +2,86 @@
<% require_image = @form.banner_image? ? false : true %> <%# Upload Banner Image %> - <%= f.input :banner_image, as: :file, wrapper: :vertical_file_input, required: require_image, hint: t('hyrax.admin.appearances.show.forms.banner_image.hint') %> + <%= f.input :banner_image, as: :file, wrapper: :vertical_file_input, required: require_image, hint: t('hyrax.admin.appearances.show.forms.banner_image.hint').html_safe, input_html: { name: 'admin_appearance[banner_image]' } %> <%= f.input :banner_image_text, required: true, as: :text, label: 'Banner image alt text' %> - <%= image_tag @form.banner_image.url, class: "img-responsive" if @form.banner_image? %> + + + +
+
+ Banner Image Preview Area +
+ <% end %> + <% if @form.banner_image? %> -<% end %> \ No newline at end of file +<% end %> + +<%# TODO: move this into the assets folder and make it work %> + diff --git a/app/views/hyrax/admin/features/index.html.erb b/app/views/hyrax/admin/features/index.html.erb new file mode 100644 index 000000000..f1bd85cc3 --- /dev/null +++ b/app/views/hyrax/admin/features/index.html.erb @@ -0,0 +1,76 @@ +<% + # OVERRIDE Hyrax 3.6.0 to make the default PDF viewer switch more intuitive and some styling changes. + # Instead of saying on/off it says PDF.js/IIIF Print. +%> + +<% provide :page_header do %> +

<%= t('.header') %>

+<% end %> +
+
+
+
+
+ + + + + + + + + + + <% @feature_set.grouped_features.each do |group, features| -%> + <% if @feature_set.grouped? -%> + + + + + <% end -%> + <% features.each do |feature| %> + + + + + + <% @feature_set.strategies.each do |strategy| -%> + <% next unless strategy.is_a? Flipflop::Strategies::ActiveRecordStrategy %> + <%# OVERRIDE to add min-width so Actions column can display all toggles on the same line %> + <%# adjust min-width as needed for future overrides %> + + <% end -%> + + <% end -%> + <% end -%> + +
<%= t('.feature') %><%= t('.description') %><%= t('.action') %>
+

+ <%= t(group ? group.name : :default, scope: [:flipflop, :groups], default: group ? group.title : nil) -%> +

+
+ <%= status_for(feature) -%> + <%= feature.name.humanize -%><%= feature.description -%> +
+ <%= form_tag(hyrax.admin_feature_strategy_path(feature.key, strategy.key), method: :put) do -%> +
+ <%# OVERRIDE to use helper, see FeaturesHelper %> + <%= submit_tag on(feature.name.to_sym), + type: "submit", + class: Flipflop.enabled?(feature.name.to_sym) ? 'active' : nil, + disabled: !strategy.switchable? -%> + + <%# OVERRIDE to use helper, see FeaturesHelper %> + <%= submit_tag off(feature.name.to_sym), + type: "submit", + class: Flipflop.enabled?(feature.name.to_sym) ? nil : 'active', + disabled: !strategy.switchable? -%> +
+ <% end -%> +
+
+
+
+
+
+
diff --git a/app/views/hyrax/base/_analytics_button.html.erb b/app/views/hyrax/base/_analytics_button.html.erb new file mode 100644 index 000000000..bb1da454d --- /dev/null +++ b/app/views/hyrax/base/_analytics_button.html.erb @@ -0,0 +1,5 @@ +<% if Hyrax.config.analytics? %> + <% # turbolinks needs to be turned off or the page will use the cache and the %> + <% # analytics graph will not show unless the page is refreshed. %> + <%= link_to t('.analytics'), @presenter.stats_path, id: 'stats', class: 'btn btn-default btn-block center-block', data: { turbolinks: false } %> +<% end %> \ No newline at end of file diff --git a/app/views/hyrax/base/_download_pdf.html.erb b/app/views/hyrax/base/_download_pdf.html.erb new file mode 100644 index 000000000..b794520cf --- /dev/null +++ b/app/views/hyrax/base/_download_pdf.html.erb @@ -0,0 +1,14 @@ +<% if can?(:download, file_set_id) && (Site.account.settings[:allow_downloads].nil? || Site.account.settings[:allow_downloads].to_i.nonzero?) %> +
+ <% if @presenter.representative_presenter.present? && presenter.file_set_presenters.any?(&:pdf?) %> + <%= button_tag type: 'button', + id: "download-pdf-button", + data: { label: @presenter.representative_presenter.id, + path: hyrax.download_path(presenter.representative_presenter) }, + class: "btn btn-success btn-block download-pdf-button center-block", + onclick: "window.open(this.dataset.path, '_blank');" do %> + Download PDF + <% end %> + <% end %> +
+<% end %> diff --git a/app/views/hyrax/base/_form_files.html.erb b/app/views/hyrax/base/_form_files.html.erb new file mode 100644 index 000000000..d4017615f --- /dev/null +++ b/app/views/hyrax/base/_form_files.html.erb @@ -0,0 +1,69 @@ +<%# OVERRIDE HYRAX 3.6.0 to add show_pdf_viewer to files tab%> +<% if render_show_pdf_behavior_checkbox? %> + <%= render partial: 'show_pdf_viewer', locals: { f: f } %> + <%= render partial: 'show_pdf_download_button', locals: { f: f } %> +<% end %> +
+ + + + + <% if Hyrax.config.browse_everything? %> + <%= t('hyrax.base.form_files.local_upload_browse_everything_html', contact_href: link_to(t("hyrax.upload.alert.contact_href_text"), hyrax.contact_form_index_path)) %> + <% else %> + <%= t('hyrax.base.form_files.local_upload_html') %> + <% end %> + +
+
+
+
+ + +
+
+ + +
+ <% if Hyrax.config.browse_everything? %> + <%= button_tag(type: 'button', class: 'btn btn-success', id: "browse-btn", + 'data-toggle' => 'browse-everything', 'data-route' => browse_everything_engine.root_path, + 'data-target' => "#{f.object.persisted? ? "#edit_#{f.object.model.model_name.param_key}_#{f.object.model.id}" : "#new_#{f.object.model.model_name.param_key}"}" ) do %> + + <%= t('hyrax.upload.browse_everything.browse_files_button') %> + <% end %> + <% end %> + + + +
+
+
+
+ +
+ +
+
+
+ +
 
+
+
+
+
+
+ <%= t('hyrax.base.form_files.dropzone') %> +
+
+ +<%= render 'hyrax/uploads/js_templates' %> diff --git a/app/views/hyrax/base/_pdf_js.erb b/app/views/hyrax/base/_pdf_js.erb new file mode 100644 index 000000000..0b65cdca3 --- /dev/null +++ b/app/views/hyrax/base/_pdf_js.erb @@ -0,0 +1,7 @@ +
+ +
diff --git a/app/views/hyrax/base/_relationships.html.erb b/app/views/hyrax/base/_relationships.html.erb new file mode 100644 index 000000000..72727e6c3 --- /dev/null +++ b/app/views/hyrax/base/_relationships.html.erb @@ -0,0 +1,39 @@ +<%# OVERRIDE Hyrax 2.9 to make relationships only show if they exist %> +<% if !current_user && presenter.grouped_presenters.present? %> + <%# Collection %> +
+

<%= t('hyrax.base.show.relationships') %>

+ <%= render 'relationships_parent_rows', presenter: presenter %> +
+<% end %> + +<% if current_user %> +
+

<%= t('hyrax.base.show.relationships') %>

+ <%# Admin Set %> + <%= presenter.attribute_to_html(:admin_set, render_as: :faceted, html_dl: true) %> + + <%# Collection %> + <% presenter.grouped_presenters(except: presenter.presenter_types).each_pair do |model_name, items| %> + <%= render 'relationships_parent_row', type: model_name, items: items, presenter: presenter %> + <% end %> + <%# Render grouped presenters. Show rows if there are any items of that type %> + <% presenter.presenter_types.each do |type| %> + <% presenter.grouped_presenters(filtered_by: type).each_pair do |_, items| %> + <%= render 'relationships_parent_row', type: type, items: items, presenter: presenter %> + <% end %> + <% end %> + + <%# Parent Work %> + <%# Render a link back to its parent works %> + <% if presenter.parent_works(current_user).present? %> + <%= render 'relationships_parent_works_rows', presenter: presenter %> + <% end %> +
+<% else %> + <%# Parent Work %> + <%# Render a link back to its parent works %> + <% if presenter.parent_works(current_user).present? %> + <%= render 'relationships_parent_works_rows', presenter: presenter %> + <% end %> +<% end %> diff --git a/app/views/hyrax/base/_relationships_parent_works_rows.html.erb b/app/views/hyrax/base/_relationships_parent_works_rows.html.erb new file mode 100644 index 000000000..4a03ec6e6 --- /dev/null +++ b/app/views/hyrax/base/_relationships_parent_works_rows.html.erb @@ -0,0 +1,12 @@ +
<%= t('hyrax.base.part_of.label') %>
+
+ +
    + <% presenter.parent_works(current_user).each do |parent_doc| %> +
  • + <%= link_to parent_doc.title.first, parent_path(parent_doc) %> +
  • + <% end %> +
+ +
diff --git a/app/views/hyrax/base/_representative_media.html.erb b/app/views/hyrax/base/_representative_media.html.erb new file mode 100644 index 000000000..b57557678 --- /dev/null +++ b/app/views/hyrax/base/_representative_media.html.erb @@ -0,0 +1,12 @@ +<% if presenter.representative_id.present? && presenter.representative_presenter.present? %> + <% if defined?(viewer) && viewer && presenter.iiif_viewer?%> + <%= iiif_viewer_display presenter %> + <% elsif Flipflop.default_pdf_viewer? && presenter.show_pdf_viewer? && presenter.file_set_presenters.any?(&:pdf?) %> + <%= render 'pdf_js', file_set_presenter: presenter.file_set_presenters.first %> + <% else %> + <%= render media_display_partial(presenter.representative_presenter), file_set: presenter.representative_presenter %> + <% end %> +<% else %> + <% alt = block_for(name: 'default_work_image_text') || 'Default work thumbnail' %> + <%= image_tag default_work_image, class: "canonical-image", alt: alt %> +<% end %> diff --git a/app/views/hyrax/base/_show_actions.html.erb b/app/views/hyrax/base/_show_actions.html.erb index b726f7dba..eb9c0e049 100644 --- a/app/views/hyrax/base/_show_actions.html.erb +++ b/app/views/hyrax/base/_show_actions.html.erb @@ -38,11 +38,6 @@ data: { behavior: 'unfeature' }, class: presenter.display_feature_link? ? 'btn btn-default collapse' : 'btn btn-default' %> <% end %> - <% if Hyrax.config.analytics? %> - <% # turbolinks needs to be turned off or the page will use the cache and the %> - <% # analytics graph will not show unless the page is refreshed. %> - <%= link_to t('.analytics'), presenter.stats_path, id: 'stats', class: 'btn btn-default', data: { turbolinks: false } %> - <% end %> diff --git a/app/views/hyrax/base/_show_pdf_download_button.erb b/app/views/hyrax/base/_show_pdf_download_button.erb new file mode 100644 index 000000000..aa326db70 --- /dev/null +++ b/app/views/hyrax/base/_show_pdf_download_button.erb @@ -0,0 +1,4 @@ +<%= f.input :show_pdf_download_button, + label: "Show Download PDF button", + required: f.object.required?(:show_pdf_download_button), + as: :boolean %> diff --git a/app/views/hyrax/base/_show_pdf_viewer.html.erb b/app/views/hyrax/base/_show_pdf_viewer.html.erb new file mode 100644 index 000000000..a2478201b --- /dev/null +++ b/app/views/hyrax/base/_show_pdf_viewer.html.erb @@ -0,0 +1,4 @@ +<%= f.input :show_pdf_viewer, + label: "Show PDF.js Viewer", + required: f.object.required?(:show_pdf_viewer), + as: :boolean %> diff --git a/app/views/hyrax/base/show.html.erb b/app/views/hyrax/base/show.html.erb index 01ccf18ca..725db7d53 100644 --- a/app/views/hyrax/base/show.html.erb +++ b/app/views/hyrax/base/show.html.erb @@ -19,11 +19,16 @@
<%= render 'representative_media', presenter: @presenter, viewer: true %>
+ <% elsif @presenter.show_pdf_viewer? %> +
+ <%= render 'pdf_js', file_set_presenter: @presenter.file_set_presenters.first %> +
<% end %>
- <%= render 'representative_media', presenter: @presenter, viewer: false unless @presenter.iiif_viewer? %> + <%= render 'representative_media', presenter: @presenter, viewer: false unless @presenter.iiif_viewer? || @presenter.show_pdf_viewer? %> + <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> <%= render 'citations', presenter: @presenter %> - <%= render 'social_media' %> + <%= render 'analytics_button', presenter: @presenter %>
<%= render 'work_description', presenter: @presenter %> diff --git a/app/views/hyrax/file_sets/_actions.html.erb b/app/views/hyrax/file_sets/_actions.html.erb new file mode 100644 index 000000000..ea6941a01 --- /dev/null +++ b/app/views/hyrax/file_sets/_actions.html.erb @@ -0,0 +1,58 @@ +<%# Overridden from Hyrax 3.5.0 - To add extra download restrictions %> +<% if (can?(:download, file_set.id) || can?(:destroy, file_set.id) || can?(:edit, file_set.id)) && !workflow_restriction?(@parent) %> + <% if can?(:download, file_set.id) && !(can?(:edit, file_set.id) || can?(:destroy, file_set.id)) %> + <% if (Site.account.settings[:allow_downloads].nil? || Site.account.settings[:allow_downloads].to_i.nonzero?) && + (@presenter.show_pdf_download_button? && file_set.pdf? || !file_set.pdf?) %> + <%= link_to t('.download'), + hyrax.download_path(file_set), + class: 'btn btn-default btn-sm', + title: t('.download_title', file_set: file_set), + target: "_blank", + id: "file_download", + data: { label: file_set.id, work_id: @presenter.id, collection_ids: @presenter.member_of_collection_ids } %> + <% end %> + <% else %> +
+ + + +
+ <% end %> +<% end %> diff --git a/app/views/hyrax/file_sets/media_display/_pdf.html.erb b/app/views/hyrax/file_sets/media_display/_pdf.html.erb new file mode 100644 index 000000000..c34bb55d2 --- /dev/null +++ b/app/views/hyrax/file_sets/media_display/_pdf.html.erb @@ -0,0 +1,17 @@ +<%# Overridden from Hyrax 3.5.0 - To add extra download restrictions %> +<% if display_media_download_link?(file_set: file_set) && (Site.account.settings[:allow_downloads].nil? || Site.account.settings[:allow_downloads].to_i.nonzero?) %> +
+

<%= t('hyrax.file_set.show.downloadable_content.heading') %>

+ <%= image_tag thumbnail_url(file_set), + class: "representative-media", + alt: "", + role: "presentation" %> +
+<% else %> +
+ <%= image_tag thumbnail_url(file_set), + class: "representative-media", + alt: "", + role: "presentation" %> +
+<% end %> diff --git a/app/views/records/edit_fields/_default.html.erb b/app/views/records/edit_fields/_default.html.erb new file mode 100644 index 000000000..0e484100f --- /dev/null +++ b/app/views/records/edit_fields/_default.html.erb @@ -0,0 +1,19 @@ +<%# OVERRIDE: HydraEditor 5.0.5 support dynamic labels and hints for custom worktypes %> +<%# Avoid NoMethod error when rendering partial in Collection or batch edit form %> +<%# Hide unwanted fields from the form %> +<% return if f.object.try(:hidden?, key) %> + +<% record = f.object.model %> +<% if f.object.multiple? key %> + <%= f.input key, + as: :multi_value, + label: label_for(term: key, record_class: record.class), + hint: hint_for(term: key, record_class: record.class), + input_html: { class: 'form-control' }, + required: f.object.required?(key) %> +<% else %> + <%= f.input key, + label: label_for(term: key, record_class: record.class), + hint: hint_for(term: key, record_class: record.class), + required: f.object.required?(key) %> +<% end %> diff --git a/app/views/shared/_appearance_styles.html.erb b/app/views/shared/_appearance_styles.html.erb index cb07843ad..43efe1931 100644 --- a/app/views/shared/_appearance_styles.html.erb +++ b/app/views/shared/_appearance_styles.html.erb @@ -43,7 +43,13 @@ body.public-facing .image-masthead .navbar .active > a:focus { background-color: <%= appearance.navbar_background_color_active %>; color: <%= appearance.navbar_link_text_color %>; } + +body.public-facing .image-masthead .navbar .navbar-nav li.active a { + background-color: <%= appearance.navbar_link_background_color_active %>; +} + body.public-facing .image-masthead .navbar .navbar-nav a { + background-color: <%= appearance.navbar_link_background_color %>; color: <%= appearance.navbar_link_text_color %>; } diff --git a/app/views/themes/cultural_repository/layouts/homepage.html.erb b/app/views/themes/cultural_repository/layouts/homepage.html.erb index e2e20c18a..1f07ec851 100644 --- a/app/views/themes/cultural_repository/layouts/homepage.html.erb +++ b/app/views/themes/cultural_repository/layouts/homepage.html.erb @@ -2,8 +2,7 @@ <% content_for(:navbar) do %>
-
- +
')">
<% if controller_name == 'homepage' %>
diff --git a/app/views/themes/cultural_show/hyrax/base/_relationships.html.erb b/app/views/themes/cultural_show/hyrax/base/_relationships.html.erb index 0a73cb5cd..09e014a01 100644 --- a/app/views/themes/cultural_show/hyrax/base/_relationships.html.erb +++ b/app/views/themes/cultural_show/hyrax/base/_relationships.html.erb @@ -1,9 +1,11 @@ <% if !current_user && presenter.grouped_presenters.present? %> + <%# Collection %>

<%= t('hyrax.base.show.relationships') %>

<%= render 'relationships_parent_rows', presenter: presenter %>
<% end %> + <% if current_user %>

<%= t('hyrax.base.show.relationships') %>

@@ -17,5 +19,15 @@ <%= render 'relationships_parent_row', type: type, items: items, presenter: presenter %> <% end %> <% end %> + <%# Render a link back to its parent works %> + <% if presenter.parent_works(current_user).present? %> + <%= render 'relationships_parent_works_rows', presenter: presenter %> + <% end %>
+<% else %> + <%# Parent Work %> + <%# Render a link back to its parent works %> + <% if presenter.parent_works(current_user).present? %> + <%= render 'relationships_parent_works_rows', presenter: presenter %> + <% end %> <% end %> diff --git a/app/views/themes/cultural_show/hyrax/base/show.html.erb b/app/views/themes/cultural_show/hyrax/base/show.html.erb index e69ec0e64..205aaa468 100644 --- a/app/views/themes/cultural_show/hyrax/base/show.html.erb +++ b/app/views/themes/cultural_show/hyrax/base/show.html.erb @@ -11,31 +11,37 @@
<%= render 'workflow_actions_widget', presenter: @presenter %> - <% if @presenter.universal_viewer? %> +
+ <%= render "show_actions", presenter: @presenter %> +
+ <% if @presenter.iiif_viewer? %>
<%= render 'representative_media', presenter: @presenter, viewer: true %>
+ <% elsif @presenter.show_pdf_viewer? %> +
+ <%= render 'pdf_js', file_set_presenter: pdf_file_set_presenter(@presenter) %> +
+ <% else %> +
+ <%= render 'representative_media', presenter: @presenter, viewer: false %> +
<% end %> -
- <%= render "show_actions", presenter: @presenter %> -
-
- <%= render 'representative_media', presenter: @presenter, viewer: false unless @presenter.universal_viewer? %> -
-
+
<%= render 'work_description', presenter: @presenter %> <%= render 'metadata', presenter: @presenter %>
-
+
<%= render 'relationships', presenter: @presenter %>
+ <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> + <%= render 'citations', presenter: @presenter %> + + <%#= render 'analytics_button', presenter: @presenter %>
<%= render 'items', presenter: @presenter %> - <%# TODO: we may consider adding these partials in the future %> - <%# = render 'sharing_with', presenter: @presenter %> - <%# = render 'user_activity', presenter: @presenter %>
diff --git a/app/views/themes/image_show/hyrax/base/show.html.erb b/app/views/themes/image_show/hyrax/base/show.html.erb new file mode 100644 index 000000000..17475eaba --- /dev/null +++ b/app/views/themes/image_show/hyrax/base/show.html.erb @@ -0,0 +1,65 @@ +<% content_for(:extra_body_classes, 'works-show ') %> + +<% provide :page_title, @presenter.page_title %> + +<%= render 'shared/citations' %> +<%= render './shared/additional_citations' %> +
+
+ <%= render 'work_type', presenter: @presenter %> +
+
+
+
+ <%= render 'work_title', presenter: @presenter %> +
+
+
+ <%= render 'workflow_actions_widget', presenter: @presenter %> + <% if @presenter.universal_viewer? %> +
+ <%= render 'representative_media', presenter: @presenter, viewer: true %> +
+
+
+ <%= render 'work_description', presenter: @presenter %> +
+
+ <% elsif Flipflop.default_pdf_viewer? && @presenter.show_pdf_viewer? && @presenter.file_set_presenters.any?(&:pdf?) %> +
+ <%= render 'pdf_js', file_set_presenter: pdf_file_set_presenter(@presenter) %> +
+ <% else %> +
+ <%= render 'representative_media', presenter: @presenter, viewer: false unless @presenter.universal_viewer? || @presenter.show_pdf_viewer? %> +
+
+ <%= render 'work_description', presenter: @presenter %> +
+ <% end %> +
+
+ +
+ <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> + <%= render 'citations', presenter: @presenter %> + + <%#= render 'analytics_button', presenter: @presenter %> +
+
+ <%= render 'relationships', presenter: @presenter %> + <% if @presenter.class == Hyrax::OerPresenter %> + <%= render 'hyrax/oers/related_items', presenter: @presenter %> + <% end %> + <%= render 'items', presenter: @presenter %> + <%# TODO: we may consider adding these partials in the future %> + <%# = render 'sharing_with', presenter: @presenter %> + <%# = render 'user_activity', presenter: @presenter %> +
+
+
+
+
+
diff --git a/app/views/themes/institutional_repository/hyrax/homepage/_resource_type_stats.html.erb b/app/views/themes/institutional_repository/hyrax/homepage/_resource_type_stats.html.erb index b8f0476e7..01d54c28d 100644 --- a/app/views/themes/institutional_repository/hyrax/homepage/_resource_type_stats.html.erb +++ b/app/views/themes/institutional_repository/hyrax/homepage/_resource_type_stats.html.erb @@ -1,12 +1,14 @@ \ No newline at end of file +
diff --git a/app/views/themes/institutional_repository/hyrax/homepage/all_collections.html.erb b/app/views/themes/institutional_repository/hyrax/homepage/all_collections.html.erb index df3994bbe..15ab236e1 100644 --- a/app/views/themes/institutional_repository/hyrax/homepage/all_collections.html.erb +++ b/app/views/themes/institutional_repository/hyrax/homepage/all_collections.html.erb @@ -1,6 +1,6 @@

Browse by Collection

-<% top_level_collections = @collections.select{ |c| c['nesting_collection__deepest_nested_depth_isi'] == 1 }%> -<% top_level_collections.each do |collection| %> + +<% @top_level_collections.each do |collection| %>
- <% roles = @presenter.user_roles(user) %> + <% roles = @presenter.user_group_roles(user) %>
    <% roles.each do |role| %> -
  • <%= role.titleize %>
  • +
  • <%= role.name.titleize %>
  • <% end %>
+ + <% roles = @presenter.user_site_roles(user) %> +
    + <% roles.each do |role| %> +
  • + <%= role.name.titleize %> + <%= link_to main_app.remove_role_admin_user_path(id: user.id, role_id: role.id), method: :delete, data: { confirm: t('hyrax.admin.users.roles.remove.confirmation', user: user.email, role: role.name.titleize) } do %> + <% if current_ability.admin? || (can?(:edit, User) && role.name != 'admin') %> + + <% end %> + <% end %> +
  • + <% end %> +
+ + <%# in the case that a user is created who never signs in, this is necessary %> diff --git a/app/views/hyrax/base/_form.html.erb b/app/views/hyrax/base/_form.html.erb new file mode 100644 index 000000000..38a9862c2 --- /dev/null +++ b/app/views/hyrax/base/_form.html.erb @@ -0,0 +1,24 @@ +<%# OVERRIDE Hyrax 3.6.0 to remove the broken error alerts from form validations %> + +<%= simple_form_for [main_app, @form], + html: { + data: { behavior: 'work-form', + 'param-key' => @form.model_name.param_key }, + multipart: true + } do |f| %> + <%# OVERRIDE Here to remove broken error alert from form validation %> + <% if Flipflop.batch_upload? && !f.object.persisted? %> + <% provide :metadata_tab do %> +

<%= t('.batch_upload_hint') %> <%= link_to t('.batch_link'), hyrax.new_batch_upload_path(payload_concern: @form.model.class) %>

+ <% end %> + <% end %> + <%= render 'hyrax/base/guts4form', f: f, tabs: form_tabs_for(form: f.object) %> +<% end %> + + \ No newline at end of file diff --git a/app/views/hyrax/base/_video_embed_viewer.html.erb b/app/views/hyrax/base/_video_embed_viewer.html.erb new file mode 100644 index 000000000..05ca0a939 --- /dev/null +++ b/app/views/hyrax/base/_video_embed_viewer.html.erb @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/app/views/hyrax/base/show.html.erb b/app/views/hyrax/base/show.html.erb index 725db7d53..5bcdd90dd 100644 --- a/app/views/hyrax/base/show.html.erb +++ b/app/views/hyrax/base/show.html.erb @@ -1,3 +1,6 @@ +<%# OVERRIDE: Hyrax 3.4.1 to remove social media %> +<%# to add work-show class to works pages %> + <% content_for(:extra_body_classes, 'works-show ') %> <% provide :page_title, @presenter.page_title %> @@ -15,21 +18,31 @@
<%= render 'workflow_actions_widget', presenter: @presenter %> - <% if @presenter.iiif_viewer? %> + <% if @presenter.video_embed_viewer? %> + <%= render 'video_embed_viewer', presenter: @presenter %> + <% elsif @presenter.iiif_viewer? %>
<%= render 'representative_media', presenter: @presenter, viewer: true %>
<% elsif @presenter.show_pdf_viewer? %>
- <%= render 'pdf_js', file_set_presenter: @presenter.file_set_presenters.first %> + <%= render 'pdf_js', file_set_presenter: pdf_file_set_presenter(@presenter) %> +
+ <% else %> +
+ <%= render 'representative_media', presenter: @presenter, viewer: false %> + <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> + <%= render 'citations', presenter: @presenter %> +
+ <% end %> + <% if @presenter.viewer? %> +
+ <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> + <%= render 'citations', presenter: @presenter %> + + <%#= render 'analytics_button', presenter: @presenter %>
<% end %> -
- <%= render 'representative_media', presenter: @presenter, viewer: false unless @presenter.iiif_viewer? || @presenter.show_pdf_viewer? %> - <%= render('download_pdf', presenter: @presenter, file_set_id: @presenter.file_set_presenters.first.id) if @presenter.show_pdf_download_button? %> - <%= render 'citations', presenter: @presenter %> - <%= render 'analytics_button', presenter: @presenter %> -
<%= render 'work_description', presenter: @presenter %> <%= render 'metadata', presenter: @presenter %> @@ -60,11 +73,10 @@ <%# = render 'sharing_with', presenter: @presenter %> <%# = render 'user_activity', presenter: @presenter %> - - <% @presenter.member_of_collection_ids.each do |collection_id| %> - - <% end %> - + + <% @presenter.member_of_collection_ids.each do |collection_id| %> + + <% end %>
diff --git a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb index ec2bce02c..bc33204a1 100644 --- a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb +++ b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb @@ -3,61 +3,59 @@ <% if can? :manage, Site %>
  • <%= menu.collapsable_section t('hyrax.admin.sidebar.settings'), - icon_class: "fa fa-cog", - id: 'collapseSettings', - open: menu.settings_section?, - title: t('hyrax.admin.sidebar.settings') do %> - <%= menu.nav_link(main_app.edit_admin_account_path, - title: t('hyrax.admin.sidebar.account')) do %> + icon_class: "fa fa-cog", + id: 'collapseSettings', + open: menu.settings_section? do %> + + <%= menu.nav_link(main_app.edit_admin_account_path) do %> <%= t('hyrax.admin.sidebar.account') %> <% end %> - <%= menu.nav_link(main_app.identity_providers_path) do %> - <%= t('hyrax.admin.sidebar.identity_providers') %> + <% if Flipflop.show_identity_provider_in_admin_dashboard? %> + <%= menu.nav_link(main_app.identity_providers_path) do %> + <%= t('hyrax.admin.sidebar.identity_providers') %> + <% end %> <% end %> - <%= menu.nav_link(main_app.edit_site_labels_path, - title: t('hyrax.admin.sidebar.labels')) do %> + <%= menu.nav_link(main_app.edit_site_labels_path) do %> <%= t('hyrax.admin.sidebar.labels') %> <% end %> <% if can?(:update, :appearance) %> - <%= menu.nav_link(hyrax.admin_appearance_path, - title: t('hyrax.admin.sidebar.appearance')) do %> + <%= menu.nav_link(hyrax.admin_appearance_path) do %> <%= t('hyrax.admin.sidebar.appearance') %> <% end %> <% end %> <% if can?(:manage, :collection_types) %> - <%= menu.nav_link(hyrax.admin_collection_types_path, - title: t('hyrax.admin.sidebar.collection_types')) do %> + <%= menu.nav_link(hyrax.admin_collection_types_path) do %> <%= t('hyrax.admin.sidebar.collection_types') %> <% end %> <% end %> <% if can?(:manage, Hyrax::Feature) %> - <%= menu.nav_link(hyrax.edit_pages_path, - title: t('hyrax.admin.sidebar.pages')) do %> + <%= menu.nav_link(hyrax.edit_pages_path) do %> <%= t('hyrax.admin.sidebar.pages') %> <% end %> - <%= menu.nav_link(hyrax.edit_content_blocks_path, - title: t('hyrax.admin.sidebar.content_blocks')) do %> + <%= menu.nav_link(hyrax.edit_content_blocks_path) do %> <%= t('hyrax.admin.sidebar.content_blocks') %> <% end %> - <%= menu.nav_link(hyrax.admin_features_path, - title: t('hyrax.admin.sidebar.technical')) do %> + <%= menu.nav_link(hyrax.admin_features_path) do %> <%= t('hyrax.admin.sidebar.technical') %> <% end %> - <%= menu.nav_link('/admin/work_types/edit', - title: t('hyku.admin.work_types')) do %> + <%= menu.nav_link('/admin/work_types/edit') do %> <%= t('hyku.admin.work_types') %> <% end %> <% end %>
  • <% end %> - <% if can?(:manage, Sipity::WorkflowResponsibility) %> - <%= menu.nav_link(hyrax.admin_workflow_roles_path, - title: t('hyrax.admin.sidebar.workflow_roles')) do %> + <% if Flipflop.show_workflow_roles_menu_item_in_admin_dashboard_sidebar? && can?(:manage, Sipity::WorkflowResponsibility) %> + <%= menu.nav_link(hyrax.admin_workflow_roles_path) do %> <%= t('hyrax.admin.sidebar.workflow_roles') %> <% end %> <% end # end of configuration block %> <%= render 'hyrax/dashboard/sidebar/menu_partials', menu: menu, section: :configuration %> <% end %> + <% if can?(:update, RolesService) %> + <%= menu.nav_link(main_app.admin_roles_service_jobs_path) do %> + <%= t('hyrax.admin.sidebar.roles_service_jobs') %> + <% end %> + <% end %> <% end %> diff --git a/app/views/hyrax/my/_search_header.html.erb b/app/views/hyrax/my/_search_header.html.erb new file mode 100644 index 000000000..fd0537876 --- /dev/null +++ b/app/views/hyrax/my/_search_header.html.erb @@ -0,0 +1,17 @@ +<%# OVERRIDE Hyrax 3.6.0 to add catalog/sort_widget %> +<%= render 'did_you_mean' %> + +<%= render 'facets' %> +<%= render 'constraints' %> + +
    +
    + <%= render 'hyrax/my/sort_and_per_page' %> + <%= render 'catalog/sort_widget' %> +
    +
    + <%= render 'search_form' %> +
    +
    + +<%= render 'batch_actions' %> diff --git a/app/views/layouts/hyrax/dashboard.html.erb b/app/views/layouts/hyrax/dashboard.html.erb new file mode 100644 index 000000000..5757dc589 --- /dev/null +++ b/app/views/layouts/hyrax/dashboard.html.erb @@ -0,0 +1,41 @@ +<%# OVERRIDE from Hyrax 3.6.0 to hide broken flash messages on the dashboard edit work pages %> + + + + + <%= render partial: 'layouts/head_tag_content' %> + <%= content_for(:head) %> + + + +
    + <%= link_to "Skip to Content", "#skip-to-content" %> +
    + <%= render '/masthead' %> + <%= content_for(:navbar) %> +
    + +
    + <%# OVERRIDE here to hide broken flash messages on the dashboard edit work pages. %> + <%# regex includes only the paths for works since this is the only known place flash messages are broken. %> + <%= render '/flash_msg' %> + <%= render_breadcrumbs builder: Hyrax::BootstrapBreadcrumbsBuilder %> + <% if content_for?(:page_header) %> +
    +
    + <%= yield(:page_header) %> +
    +
    + <% end %> + + + <%= content_for?(:content) ? yield(:content) : yield %> + +
    + +
    + <%= render 'shared/ajax_modal' %> + + \ No newline at end of file diff --git a/app/views/themes/cultural_show/hyrax/base/show.html.erb b/app/views/themes/cultural_show/hyrax/base/show.html.erb index 205aaa468..8c9133f90 100644 --- a/app/views/themes/cultural_show/hyrax/base/show.html.erb +++ b/app/views/themes/cultural_show/hyrax/base/show.html.erb @@ -14,20 +14,24 @@
    <%= render "show_actions", presenter: @presenter %>
    - <% if @presenter.iiif_viewer? %> -
    - <%= render 'representative_media', presenter: @presenter, viewer: true %> -
    - <% elsif @presenter.show_pdf_viewer? %> -
    - <%= render 'pdf_js', file_set_presenter: pdf_file_set_presenter(@presenter) %> -
    + <% if @presenter.video_embed_viewer? %> + <%= render 'video_embed_viewer', presenter: @presenter %> <% else %> -
    - <%= render 'representative_media', presenter: @presenter, viewer: false %> -
    + <% if @presenter.iiif_viewer? %> +
    + <%= render 'representative_media', presenter: @presenter, viewer: true %> +
    + <% elsif @presenter.show_pdf_viewer? %> +
    + <%= render 'pdf_js', file_set_presenter: pdf_file_set_presenter(@presenter) %> +
    + <% else %> +
    + <%= render 'representative_media', presenter: @presenter, viewer: false %> +
    + <% end %> <% end %> -
    +
    <%= render 'work_description', presenter: @presenter %> <%= render 'metadata', presenter: @presenter %>
    @@ -43,8 +47,12 @@
    <%= render 'items', presenter: @presenter %>
    + + <% @presenter.member_of_collection_ids.each do |collection_id| %> + + <% end %>
    -
    +
    \ No newline at end of file diff --git a/app/views/themes/scholarly_show/hyrax/base/show.html.erb b/app/views/themes/scholarly_show/hyrax/base/show.html.erb index 47b220b1c..e75e220fe 100644 --- a/app/views/themes/scholarly_show/hyrax/base/show.html.erb +++ b/app/views/themes/scholarly_show/hyrax/base/show.html.erb @@ -16,8 +16,15 @@
    <%= render 'workflow_actions_widget', presenter: @presenter %> - <% if @presenter.iiif_viewer? %> + <% if @presenter.video_embed_viewer? %> + <%= render 'video_embed_viewer', presenter: @presenter %>
    +
    + <%= render 'work_description', presenter: @presenter %> +
    +
    + <% elsif @presenter.iiif_viewer? %> +
    <%= render 'representative_media', presenter: @presenter, viewer: true %>
    @@ -60,5 +67,9 @@
    + + <% @presenter.member_of_collection_ids.each do |collection_id| %> + + <% end %>
    diff --git a/config/features.rb b/config/features.rb index 6405624b7..568069919 100644 --- a/config/features.rb +++ b/config/features.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true Flipflop.configure do + feature :show_workflow_roles_menu_item_in_admin_dashboard_sidebar, + default: false, + description: "Shows the Workflow Roles menu item in the admin dashboard sidebar." + feature :show_featured_researcher, default: true, description: "Shows the Featured Researcher tab on the homepage." @@ -24,6 +28,9 @@ # Flipflop.default_pdf_viewer? returning `true` means we use PDF.js and `false` means we use IIIF Print. feature :default_pdf_viewer, default: true, - description: "Choose PDF.js or Universal Viewer to render PDFs. UV uses IIIF Print and requires PDF spltting with OCR. Switching from PDF.js to the UV may require re-ingesting of the PDF." + description: "Choose PDF.js or Universal Viewer to render PDFs. UV uses IIIF Print and requires PDF splitting with OCR. Switching from PDF.js to the UV may require re-ingesting of the PDF." + feature :show_login_link, + default: true, + description: "Show General Login Link at Top Right of Page." end diff --git a/config/initializers/bulkrax.rb b/config/initializers/bulkrax.rb index 1f2d38b28..768d4bf08 100644 --- a/config/initializers/bulkrax.rb +++ b/config/initializers/bulkrax.rb @@ -111,5 +111,10 @@ # config.reserved_properties += ['my_field'] end + # Sidebar for hyrax 3+ support + if Object.const_defined?(:Hyrax) && ::Hyrax::DashboardController&.respond_to?(:sidebar_partials) + Hyrax::DashboardController.sidebar_partials[:repository_content] << "hyrax/dashboard/sidebar/bulkrax_sidebar_additions" + end + Bulkrax::CreateRelationshipsJob.update_child_records_works_file_sets = true end diff --git a/config/routes.rb b/config/routes.rb index a960e3203..82a22d600 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,8 +12,8 @@ mount Hyrax::IiifAv::Engine, at: '/' mount Riiif::Engine => 'images', as: :riiif if Hyrax.config.iiif_image_server? - authenticate :user, ->(u) { u.is_superadmin || u.is_admin } do - mount Sidekiq::Web => '/jobs' + authenticate :user, ->(u) { u.is_superadmin } do + mount Sidekiq::Web => '/sidekiq' end if ActiveModel::Type::Boolean.new.cast(ENV.fetch('HYKU_MULTITENANT', false)) @@ -27,7 +27,11 @@ namespace :proprietor do resources :accounts - resources :users + resources :users do + member do + post :become + end + end end end end @@ -35,6 +39,7 @@ get 'status', to: 'status#index' mount BrowseEverything::Engine => '/browse' + resource :site, only: [:update] do resources :roles, only: %i[index update] resource :labels, only: %i[edit update] @@ -70,6 +75,7 @@ mount Qa::Engine => '/authorities' mount Blacklight::Engine => '/' + mount Hyrax::Engine, at: '/' mount Bulkrax::Engine, at: '/' if ENV.fetch('HYKU_BULKRAX_ENABLED', 'true') == 'true' @@ -102,6 +108,7 @@ resource :work_types, only: %i[edit update] resources :users, only: [:index, :destroy] do post 'activate', on: :member + delete 'remove_role/:role_id', on: :member, to: 'users#remove_role', as: :remove_role end resources :groups do member do @@ -111,6 +118,8 @@ resources :users, only: %i[index create destroy], param: :user_id, controller: 'group_users' resources :roles, only: %i[index create destroy], param: :role_id, controller: 'group_roles' end + post "roles_service/:job_name_key", to: "roles_service#update_roles", as: :update_roles + get "roles_service", to: "roles_service#index", as: :roles_service_jobs end # OVERRIDE here to add featured collection routes diff --git a/config/tinymce.yml b/config/tinymce.yml index ed2b4c001..8e23ebf1c 100644 --- a/config/tinymce.yml +++ b/config/tinymce.yml @@ -3,11 +3,15 @@ default: &default content_block: <<: *default menubar: false - toolbar1: styleselect | bold italic | undo redo - toolbar2: table | fullscreen | image + image_uploadtab: true + toolbar1: styleselect fontselect fontsizeselect | backcolor forecolor | bold italic underline + toolbar2: indent outdent | alignleft aligncenter alignright | table | fullscreen | link hr image | undo redo | code plugins: - table - fullscreen + - link + - autolink - image + - code custom: <<: *default diff --git a/spec/controllers/admin/roles_service_controller_spec.rb b/spec/controllers/admin/roles_service_controller_spec.rb new file mode 100644 index 000000000..0cb2b98b8 --- /dev/null +++ b/spec/controllers/admin/roles_service_controller_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +RSpec.describe Admin::RolesServiceController, type: :controller do + context 'as an anonymous user' do + describe 'GET #index' do + subject { get :index } + + it { is_expected.to redirect_to new_user_session_path } + end + end + + context 'as an admin user' do + before { sign_in create(:admin) } + + describe 'GET #index' do + subject { get :index } + + it { is_expected.to render_template('layouts/hyrax/dashboard') } + it { is_expected.to render_template('admin/roles_service/index') } + end + end + + context 'as an admin user' do + before { sign_in create(:admin) } + + describe 'GET #index' do + subject { get :index } + + it { is_expected.to render_template('layouts/hyrax/dashboard') } + it { is_expected.to render_template('admin/roles_service/index') } + end + + describe 'POST #update_roles' do + it 'submits a job when it receives a valid job name' do + expect(RolesService::CreateCollectionAccessesJob).to receive(:perform_later) + post :update_roles, params: { job_name_key: :create_collection_accesses } + end + end + end +end diff --git a/spec/controllers/hyrax/my/collections_controller_spec.rb b/spec/controllers/hyrax/my/collections_controller_spec.rb new file mode 100644 index 000000000..370b7f453 --- /dev/null +++ b/spec/controllers/hyrax/my/collections_controller_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +RSpec.describe Hyrax::My::CollectionsController, type: :controller do + describe "#configure_facets" do + subject { controller.blacklight_config.sort_fields.keys } + + let(:expected_sort_fields) do + [ + "system_modified_dtsi desc", + "system_modified_dtsi asc", + "system_create_dtsi desc", + "system_create_dtsi asc", + "depositor_ssi asc, title_ssi asc", + "depositor_ssi desc, title_ssi desc", + "creator_ssi asc, title_ssi asc", + "creator_ssi desc, title_ssi desc" + ] + end + + it "configures the custom sort fields" do + expect(subject).to match_array(expected_sort_fields) + end + end +end diff --git a/spec/controllers/hyrax/my/works_controller_spec.rb b/spec/controllers/hyrax/my/works_controller_spec.rb new file mode 100644 index 000000000..52874d159 --- /dev/null +++ b/spec/controllers/hyrax/my/works_controller_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +RSpec.describe Hyrax::My::WorksController, type: :controller do + describe "#configure_facets" do + subject { controller.blacklight_config.sort_fields.keys } + + let(:expected_sort_fields) do + [ + "date_uploaded_dtsi desc", + "date_uploaded_dtsi asc", + "date_modified_dtsi desc", + "date_modified_dtsi asc", + "system_create_dtsi desc", + "system_create_dtsi asc", + "depositor_ssi asc, title_ssi asc", + "depositor_ssi desc, title_ssi desc", + "creator_ssi asc, title_ssi asc", + "creator_ssi desc, title_ssi desc" + ] + end + + it "configures the custom sort fields" do + expect(subject).to match_array(expected_sort_fields) + end + end +end diff --git a/spec/features/admin_dashboard_spec.rb b/spec/features/admin_dashboard_spec.rb index 3eef1f164..1ad7b2533 100644 --- a/spec/features/admin_dashboard_spec.rb +++ b/spec/features/admin_dashboard_spec.rb @@ -43,7 +43,7 @@ expect(page).to have_link('Content Blocks') expect(page).to have_link('Features') expect(page).to have_link('Available Work Types') - expect(page).to have_link('Workflow Roles') + # expect(page).to have_link('Workflow Roles') end end diff --git a/spec/features/manage_user_groups_and_roles_spec.rb b/spec/features/manage_user_groups_and_roles_spec.rb index e69ae8952..d6aeb8e0b 100644 --- a/spec/features/manage_user_groups_and_roles_spec.rb +++ b/spec/features/manage_user_groups_and_roles_spec.rb @@ -36,10 +36,11 @@ it "lists each user's associated direct and inherited roles" do expect(page).to have_content('Manage Users') - expect(page).to have_css 'th', text: 'Roles' - expect(find("tr##{admin.email.parameterize} td.roles")).to have_text(admin_role.name.titlecase) - expect(find("tr##{user.email.parameterize} td.roles")).to have_text(user_manager_role.name.titlecase) - expect(find("tr##{user.email.parameterize} td.roles")).to have_text(collection_manager_role.name.titlecase) + expect(page).to have_css 'th', text: 'Group roles' + expect(page).to have_css 'th', text: 'Site roles' + expect(find("tr##{admin.email.parameterize} td.group-roles")).to have_text(admin_role.name.titlecase) + expect(find("tr##{user.email.parameterize} td.group-roles")).to have_text(user_manager_role.name.titlecase) + expect(find("tr##{user.email.parameterize} td.site-roles")).to have_text(collection_manager_role.name.titlecase) end it 'can visit Manage Users and invite users with the admin role' do diff --git a/spec/helpers/blacklight/advanced_search_helper_spec.rb b/spec/helpers/blacklight/advanced_search_helper_spec.rb deleted file mode 100644 index 025c3613b..000000000 --- a/spec/helpers/blacklight/advanced_search_helper_spec.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Blacklight::AdvancedSearchHelper do - include described_class - - let(:search_fields_for_advanced_search) do - CatalogController.blacklight_config.search_fields.select do |_k, v| - v.include_in_advanced_search || v.include_in_advanced_search.nil? - end - end - - describe "#primary_search_fields" do - it "returns the first 6 advanced search fields" do - expect(primary_search_fields_for(search_fields_for_advanced_search).size).to eq(6) - end - end - - describe "#secondary_search_fields" do - it "returns the rest of the advanced search fields" do - second_search_fields_count = search_fields_for_advanced_search.size - 6 - - expect(secondary_search_fields_for(search_fields_for_advanced_search).size).to eq(second_search_fields_count) - end - end - - describe "#local_authority?" do - context 'when key is valid' do - it "checks if the key exists in QA::Authorities::Local.names" do - valid_local_authorities = search_fields_for_advanced_search.select { |key, _value| local_authority?(key) } - - valid_local_authorities.each_key do |key| - expect(local_authority?(key)).to be true - end - end - end - - context 'when the key is invalid' do - it "checks if the key exists in QA::Authorities::Local.names" do - invalid_local_authorities = search_fields_for_advanced_search.reject { |key, _value| local_authority?(key) } - - invalid_local_authorities.each_key do |key| - expect(local_authority?(key)).to be false - end - end - end - end - - describe "#options_for_qa_select?" do - it "returns the values for a given key" do - valid_local_authority_keys = search_fields_for_advanced_search.select { |key, _value| local_authority?(key) }.keys - - valid_local_authority_keys.each do |key| - local_authority_terms = fetch_local_yml(key)['terms'].map { |term| [term['term'], term['id']] } - - expect(options_for_qa_select(key)).to eq(local_authority_terms) - end - end - end - - # a utility method for finding the correct yml files because the naming is inconsistent, plural vs singular - def fetch_local_yml(key) - YAML.load_file("config/authorities/#{key}.yml") - rescue Errno::ENOENT - YAML.load_file("config/authorities/#{key.pluralize}.yml") - end - - describe "#fetch_service_for" do - it "returns the service class for a given a valid key" do - expect(fetch_service_for('rights_statement')).to eq(Hyrax::RightsStatementService) - end - - it "returns nil for an invalid key" do - expect(fetch_service_for('foo')).to be_nil - end - end -end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index ca8a439ce..aa3265943 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -91,6 +91,24 @@ it { is_expected.to be_able_to(:manage, :all) } end + describe 'a user_manager user' do + let(:user) { FactoryBot.create(:user) } + let(:ordinary_role) { FactoryBot.create(:role, name: 'ordinary_role') } + + before do + user.add_role :user_manager, Site.instance + end + + context 'when managing User and Role' do + it 'can create, read, update, and edit User and Role' do + expect(ability).to be_able_to(:create, User.new) + expect(ability).to be_able_to(:read, User.new) + expect(ability).to be_able_to(:update, User.new) + expect(ability).to be_able_to(:edit, User.new) + end + end + end + # Brought over from blacklight-access_controls v0.6.2 describe '#user_groups' do subject { ability.user_groups } diff --git a/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb b/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb new file mode 100644 index 000000000..1ede624a5 --- /dev/null +++ b/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Hyrax::IiifAv::DisplaysContentDecorator do + # We're prepending the DisplaysContentDecorator to the Hyrax::IiifAv::DisplaysContent + describe Hyrax::IiifAv::DisplaysContent do + describe '.public_instance_methods' do + subject { Hyrax::IiifAv::DisplaysContent.public_instance_methods } + + it { is_expected.to include(:solr_document) } + it { is_expected.to include(:current_ability) } + end + end +end diff --git a/spec/routing/admin/roles_service_routing_spec.rb b/spec/routing/admin/roles_service_routing_spec.rb new file mode 100644 index 000000000..d0c069865 --- /dev/null +++ b/spec/routing/admin/roles_service_routing_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +RSpec.describe Admin::RolesServiceController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "/admin/roles_service").to route_to("admin/roles_service#index") + end + + it "routes to #update_roles via POST" do + expect(post: "/admin/roles_service/create_collection_accesses") + .to route_to("admin/roles_service#update_roles", job_name_key: 'create_collection_accesses') + end + end +end diff --git a/spec/views/hyrax/admin/users/index.html.erb_spec.rb b/spec/views/hyrax/admin/users/index.html.erb_spec.rb index 14db24a44..e9e15197a 100644 --- a/spec/views/hyrax/admin/users/index.html.erb_spec.rb +++ b/spec/views/hyrax/admin/users/index.html.erb_spec.rb @@ -68,7 +68,9 @@ (5..6).each do |i| expect(page).to have_content("admin#{i}@example.com") end - expect(page).to have_selector("div.users-listing td.roles li", text: 'Admin', count: 4) + + expect(page).to have_selector("div.users-listing td.site-roles li", text: 'Admin', count: 2) + expect(page).to have_selector("div.users-listing td.group-roles li", text: 'Admin', count: 2) end end From 016669295c82b158a991d18a03533604e29a1af6 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Tue, 19 Dec 2023 12:47:42 -0500 Subject: [PATCH 03/20] Updating translations --- config/locales/blacklight.de.yml | 1 + config/locales/blacklight.en.yml | 1 + config/locales/blacklight.es.yml | 1 + config/locales/blacklight.fr.yml | 1 + config/locales/blacklight.it.yml | 1 + config/locales/blacklight.pt-BR.yml | 1 + config/locales/blacklight.zh.yml | 1 + config/locales/de.yml | 17 ++++++- config/locales/devise_invitable.en.yml | 2 +- config/locales/en.yml | 44 ++++++++++++++++- config/locales/es.yml | 18 +++++-- config/locales/fr.yml | 17 ++++++- config/locales/hyrax.de.yml | 16 +++++-- config/locales/hyrax.en.yml | 19 +++++--- config/locales/hyrax.es.yml | 16 +++++-- config/locales/hyrax.fr.yml | 9 +++- config/locales/hyrax.it.yml | 13 ++++-- config/locales/hyrax.pt-BR.yml | 9 ++-- config/locales/hyrax.zh.yml | 11 +++-- config/locales/it.yml | 18 +++++-- config/locales/pt-BR.yml | 18 +++++-- config/locales/simple_form.de.yml | 65 +++++++++----------------- config/locales/simple_form.en.yml | 3 +- config/locales/simple_form.es.yml | 48 +++++++++---------- config/locales/simple_form.fr.yml | 59 +++++++++-------------- config/locales/simple_form.it.yml | 59 +++++++++-------------- config/locales/simple_form.pt-BR.yml | 61 +++++++++--------------- config/locales/simple_form.zh.yml | 63 ++++++++++--------------- config/locales/zh.yml | 18 +++++-- 29 files changed, 343 insertions(+), 267 deletions(-) diff --git a/config/locales/blacklight.de.yml b/config/locales/blacklight.de.yml index 307055e88..8a40282c2 100644 --- a/config/locales/blacklight.de.yml +++ b/config/locales/blacklight.de.yml @@ -47,3 +47,4 @@ de: rights_tesim: Rechte subject_tesim: Fach title_tesim: Titel + start_over: Neue Suche diff --git a/config/locales/blacklight.en.yml b/config/locales/blacklight.en.yml index e5471aaf1..7fce013d2 100644 --- a/config/locales/blacklight.en.yml +++ b/config/locales/blacklight.en.yml @@ -46,3 +46,4 @@ en: rights_tesim: Rights subject_tesim: Subject title_tesim: Title + start_over: New Search diff --git a/config/locales/blacklight.es.yml b/config/locales/blacklight.es.yml index d276a26eb..4142fb765 100644 --- a/config/locales/blacklight.es.yml +++ b/config/locales/blacklight.es.yml @@ -47,3 +47,4 @@ es: rights_tesim: Derechos subject_tesim: Tema title_tesim: Título + start_over: Nueva búsqueda diff --git a/config/locales/blacklight.fr.yml b/config/locales/blacklight.fr.yml index d1f107f74..a987fcd71 100644 --- a/config/locales/blacklight.fr.yml +++ b/config/locales/blacklight.fr.yml @@ -47,3 +47,4 @@ fr: rights_tesim: Droits subject_tesim: Assujettir title_tesim: Titre + start_over: Nouvelle recherche diff --git a/config/locales/blacklight.it.yml b/config/locales/blacklight.it.yml index d6f885987..7b2cafec6 100644 --- a/config/locales/blacklight.it.yml +++ b/config/locales/blacklight.it.yml @@ -47,3 +47,4 @@ it: rights_tesim: Diritti subject_tesim: Soggetto title_tesim: Titolo + start_over: Nuova ricerca diff --git a/config/locales/blacklight.pt-BR.yml b/config/locales/blacklight.pt-BR.yml index 689970a78..2648b6a6b 100644 --- a/config/locales/blacklight.pt-BR.yml +++ b/config/locales/blacklight.pt-BR.yml @@ -47,3 +47,4 @@ pt-BR: rights_tesim: Direitos subject_tesim: Sujeito title_tesim: Título + start_over: Nova pesquisa diff --git a/config/locales/blacklight.zh.yml b/config/locales/blacklight.zh.yml index 443284900..81842f4b3 100644 --- a/config/locales/blacklight.zh.yml +++ b/config/locales/blacklight.zh.yml @@ -47,3 +47,4 @@ zh: rights_tesim: 权 subject_tesim: 学科 title_tesim: 标题 + start_over: 新搜索 diff --git a/config/locales/de.yml b/config/locales/de.yml index 55a462a33..e86a331c1 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1,5 +1,12 @@ --- de: + errors: + messages: + valid_embed_url: "muss eine gültige YouTube- oder Vimeo-Einbettungs-URL sein." + activefedora: + models: + generic_work: Arbeiten + image: Bild activerecord: attributes: site: @@ -7,6 +14,8 @@ de: application: tagline: Die Next-Generation Repository-Lösung helpers: + action: + become: Werden submit: add_role_to_group: submit: Hinzufügen @@ -162,6 +171,8 @@ de: forms: banner_image: hint: Um ein Bild als Masthead-Hintergrund zu verwenden, sollten Sie ein Bild (JPG, GIF oder PNG) verwenden, das mindestens 120 Pixel hoch und 1200 Pixel breit ist. Verwenden Sie für optimale Ergebnisse ein Bild mit einer Breite von mindestens 1800 Pixel. + collection_banner_text_color: + hint: Die Farbe für den Text (Titel und Datum der letzten Aktualisierung) im Sammlungsbanner. custom_css: confirm: Benutzerdefiniertes CSS überschreibt immer andere Themenauswahlen. Vorgehen? warning: Wenn Ihre Themenanpassungen nicht korrekt angewendet werden, stellen Sie sicher, dass sie nicht von benutzerdefiniertem CSS überschrieben werden. @@ -171,7 +182,7 @@ de: alert: Bitte laden Sie vor dem Absenden mindestens eine Datei hoch. hint: Für Standardbilder sollten Sie ein Bild (JPG, GIF oder PNG) verwenden, das die gleichen Abmessungen für Höhe und Breite aufweist (100 Pixel breit und 100 Pixel hoch). directory_image: - hint: Um ein Bild als Verzeichnisbild zu verwenden, sollten Sie ein Bild (JPG, GIF oder PNG) verwenden, das nicht höher als der Header und nicht breiter als 400 Pixel ist. + hint: Das Verzeichnisbild sollte ein Bild (JPG, GIF oder PNG) verwenden. Die Breite des Bildes sollte nicht größer als die doppelte Höhe sein. facet_panel_background_color: hint: Gilt für Facetten und zusätzliche Abschnittsüberschriften auf den Arbeitsseiten einiger Themen. facet_panel_text_color: @@ -208,7 +219,6 @@ de: fonts: Schriftarten themes: Themen sidebar: - account: Konto accounts: Konten activity_summary: Aktivitätsübersicht content_blocks: Inhaltsblöcke @@ -237,6 +247,9 @@ de: active: Aktiv pending: noch ausstehend status_label: Status + roles: + remove: + confirmation: Sind Sie sicher, dass Sie die Rolle „%{role}“ vom Benutzer „%{user}“ entfernen möchten? permissions: collections: cannot: diff --git a/config/locales/devise_invitable.en.yml b/config/locales/devise_invitable.en.yml index 61260a473..a7e17d640 100644 --- a/config/locales/devise_invitable.en.yml +++ b/config/locales/devise_invitable.en.yml @@ -22,7 +22,7 @@ en: accept_until: This invitation will be due in %{due_date}. hello: Hello %{email} ignore: |- - If you don't want to accept the invitation, please ignore this email. + If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password. someone_invited_you: Someone has invited you to %{url}, you can accept it through the link below. subject: Invitation instructions diff --git a/config/locales/en.yml b/config/locales/en.yml index 44e353a89..47f33071a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,8 @@ --- en: + errors: + messages: + valid_embed_url: "must be a valid YouTube or Vimeo Embed URL." single_signon: index: sign_in_with_provider: Sign in with %{provider} @@ -9,7 +12,18 @@ en: institution_name_full: Full institution name application: tagline: The next-generation repository solution + activefedora: + models: + generic_work: "Work" + image: "Image" + devise: + mailer: + invitation_instructions: + ignore: "If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password." + helpers: + action: + become: Become submit: add_role_to_group: submit: Add @@ -199,6 +213,8 @@ en: hint: 'Applies to facets and additional section headers on the work pages in some themes.' facet_panel_text_color: hint: 'Also applies to the color of the caret next to the text and the work title on some themes.' + collection_banner_text_color: + hint: 'The color for the text (title, and last updated date) inside of the collection banner.' tabs: banner_image: "Banner Image" directory_image: "Directory Image" @@ -208,14 +224,35 @@ en: favicon: Favicon fonts: "Fonts" themes: "Themes" + roles_service_jobs: + header: Data Repair Jobs + jobs: + create_admin_set_accesses: + label: Create Admin Set Accesses + description: "Creating a Hyrax::PermissionTemplateAccess record (combined with Ability#user_groups) + will allow Works in all AdminSets to show up in Blacklight / Solr queries." + create_collection_accesses: + label: Create Collection Accesses + description: "Because each collection role has some level of access to every Collection within a tenant, + creating a Hyrax::PermissionTemplateAccess record (combined with Ability#user_groups) + means all Collections will show up in Blacklight / Solr queries." + create_collection_type_participants: + label: Create Collection Type Participants + description: "Because some of the collection roles have access to every Collection within a tenant, create a + Hyrax::CollectionTypeParticipant record for them on every Hyrax::CollectionType (except the AdminSet)" + grant_workflow_roles_for_all_admin_sets: + label: Admin Set Workflow Roles + description: "Permissions to deposit Works are controlled by Workflow Roles on individual AdminSets. + In order for Hyrax::Group and User records who have either the 'Work Editor' or 'Work Depositor' Role + to have the correct permissions for Works, we grant them Workflow Roles for all AdminSets. + NOTE: All AdminSets must have a permission template or this will fail. Run 'Create Admin Set Accesses' first." sidebar: - account: Account accounts: Accounts activity_summary: Activity Summary labels: Labels manage_groups: Manage Groups - repository_activity: Repository Activity system_status: System Status + roles_service_jobs: Data Repair users: activate: confirmation: Are you sure you want to activate the user "%{user}"? @@ -234,6 +271,9 @@ en: active: Active pending: Pending status_label: Status + roles: + remove: + confirmation: Are you sure you want to remove the role "%{role}" from the user "%{user}"? permissions: collections: cannot: diff --git a/config/locales/es.yml b/config/locales/es.yml index 6137a7659..290ec7752 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1,5 +1,12 @@ --- es: + errors: + messages: + valid_embed_url: "debe ser una URL de incrustación válida de YouTube o Vimeo." + activefedora: + models: + generic_work: Trabajar + image: Imagen activerecord: attributes: site: @@ -7,6 +14,8 @@ es: application: tagline: La solución de repositorio de próxima generación helpers: + action: + become: Volverse submit: add_role_to_group: submit: Añadir @@ -163,6 +172,8 @@ es: forms: banner_image: hint: Para usar una imagen como fondo de cabecera, debe usar una imagen (JPG, GIF o PNG) que tenga al menos 120 píxeles de alto y 1200 píxeles de ancho. Para obtener mejores resultados, use una imagen de al menos 1800 píxeles de ancho. + collection_banner_text_color: + hint: El color del texto (título y fecha de última actualización) dentro del banner de la colección. custom_css: confirm: CSS personalizado siempre anulará otras selecciones de temas. ¿Continuar? warning: Si sus personalizaciones de temas no parecen aplicarse correctamente, verifique que CSS personalizado no las sobrescriba. @@ -172,7 +183,7 @@ es: alert: Cargue al menos un archivo antes de enviarlo. hint: Para las imágenes predeterminadas, debe usar una imagen (JPG, GIF o PNG) que tenga las mismas dimensiones de alto y ancho (100 píxeles de ancho y 100 píxeles de alto) directory_image: - hint: Para usar una imagen como imagen de directorio, debe usar una imagen (JPG, GIF o PNG) que no sea más alta que el encabezado ni más ancha que 400 píxeles de ancho. + hint: La imagen del directorio debe usar una imagen (JPG, GIF o PNG). El ancho de la imagen no debe ser más ancho que el doble de la altura. facet_panel_background_color: hint: Se aplica a facetas y encabezados de sección adicionales en las páginas de trabajo en algunos temas. facet_panel_text_color: @@ -209,12 +220,10 @@ es: fonts: Fuentes themes: Temas sidebar: - account: Cuenta accounts: Cuentas activity_summary: Resumen de la actividad labels: Etiquetas manage_groups: Administrar Grupos - repository_activity: Actividad del repositorio system_status: Estado del sistema users: activate: @@ -234,6 +243,9 @@ es: active: Activo pending: Pendiente status_label: Estado + roles: + remove: + confirmation: ¿Está seguro de que desea eliminar el rol "%{role}" del usuario "%{user}"? permissions: collections: cannot: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 0a732ce65..8517263d7 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1,5 +1,12 @@ --- fr: + errors: + messages: + valid_embed_url: "doit être une URL d'incorporation valide de YouTube ou Vimeo." + activefedora: + models: + generic_work: Travail + image: Image activerecord: attributes: site: @@ -7,6 +14,8 @@ fr: application: tagline: La solution de dépôt de nouvelle génération helpers: + action: + become: Devenir submit: add_role_to_group: submit: Ajouter @@ -163,6 +172,8 @@ fr: forms: banner_image: hint: Pour utiliser une image comme arrière-plan d'une bannière Masthead, vous devez utiliser une image (JPG, GIF ou PNG) d'au moins 120 pixels de haut et 1 200 pixels de large. Pour de meilleurs résultats, utilisez une image d'au moins 1800 pixels de large. + collection_banner_text_color: + hint: La couleur du texte (titre et date de la dernière mise à jour) à l'intérieur de la bannière de collection. custom_css: confirm: Le CSS personnalisé remplacera toujours les autres sélections de thèmes. Procéder? warning: Si vos personnalisations de thème ne semblent pas s'appliquer correctement, vérifiez qu'elles ne sont pas écrasées par CSS personnalisé. @@ -172,7 +183,7 @@ fr: alert: Veuillez télécharger au moins un fichier avant de soumettre. hint: Pour les images par défaut, vous devez utiliser une image (JPG, GIF ou PNG) qui a des dimensions de hauteur et de largeur égales (100 pixels de large et 100 pixels de haut) directory_image: - hint: Pour utiliser une image comme image de répertoire, vous devez utiliser une image (JPG, GIF ou PNG) qui ne dépasse pas l'en-tête et ne dépasse pas 400 pixels de large. + hint: L'image du répertoire doit utiliser une image (JPG, GIF ou PNG). La largeur de l'image ne doit pas être plus large que 2x la hauteur. facet_panel_background_color: hint: S'applique aux facettes et aux en-têtes de section supplémentaires sur les pages de travail dans certains thèmes. facet_panel_text_color: @@ -209,7 +220,6 @@ fr: fonts: Les Polices themes: Thèmes sidebar: - account: Compte accounts: Comptes activity_summary: Résumé de l'activité content_blocks: Blocs de contenu @@ -239,6 +249,9 @@ fr: active: actif pending: en attendant status_label: Statut + roles: + remove: + confirmation: Êtes-vous sûr de vouloir supprimer le rôle « %{role} » de l'utilisateur « %{user} » ? permissions: collections: cannot: diff --git a/config/locales/hyrax.de.yml b/config/locales/hyrax.de.yml index 34aecc98c..9f157430a 100644 --- a/config/locales/hyrax.de.yml +++ b/config/locales/hyrax.de.yml @@ -243,9 +243,11 @@ de: one: In diesem Repository befindet sich ein %{count}-Benutzer . other: In diesem Repository befinden sich %{count}-Benutzer . group_label: Gruppen + group_role_label: Gruppenrollen id_label: Nutzername reader_title: Benutzer anzeigen role_label: Rollen + site_role_label: Site-Rollen title: Benutzer verwalten workflow_roles: header: Workflow-Rollen @@ -295,7 +297,7 @@ de: background_attribution_html: '' base: citations: - header: 'Zitate' + header: Zitate form_child_work_relationships: actions: remove: Aus dieser Arbeit entfernen @@ -696,7 +698,6 @@ de: sr: batch_checkbox: Aktivieren Sie diese Option, um sie zu einer Sammlung oder Bearbeitungsliste hinzuzufügen check_all_label: Wählen Sie alle Dateien aus, die einer Sammlung hinzugefügt oder bearbeitet werden sollen - collections_batch_checkbox: Aktivieren Sie diese Option, um Sammlungen stapelweise zu löschen. detail_label: Zeigen Sie zusammenfassende Details von an listing: Auflistung der Artikel, in denen Sie hinterlegt haben press_to: drücke um zu @@ -1098,7 +1099,7 @@ de: description: Eine kurze übergreifende Beschreibung, die für alle in diesem Set gesammelten Werke gilt. Zum Beispiel "Thesen und ergänzende Dateien, die von Absolventen der School of Earth Sciences erstellt wurden." title: Ein Name, der die Identifizierung des Verwaltungssatzes erleichtert und ihn von anderen Verwaltungssätzen im Repository unterscheidet. collection: - based_near: Ein Ortsname, der sich auf die Sammlung bezieht, z. B. der Veröffentlichungsort oder die Stadt, das Bundesland oder das Land, um das es in der Sammlung geht. Ruft den GeoNames-Webdienst auf. + based_near: Ein Ortsname, der sich auf das Werk bezieht, z. B. der Ort der Veröffentlichung oder die Stadt, das Bundesland oder das Land, um das es in den Inhalten des Werks geht. Ruft die Geonames-Webdienstseite.
    2. Gehen Sie zu Ihren Geonames-Kontoeinstellungen und wählen Sie unten die Option aus, um kostenlose Dienste zu aktivieren.
    3. Gehen Sie im Dashboard
    zu den Einstellungen Ihres Hyku-Kontos. Geben Sie Ihren Geonames-Benutzernamen in den Hyrax-Kontoeinstellungen ein.
    5. Sie sollten nun die Liste der Standorte in diesem Dropdown-Menü sehen können. contributor: Eine Person oder Gruppe, die Sie als eine Rolle bei der Erstellung der Sammlung erkennen möchten, jedoch nicht als primäre Rolle. creator: Die Person oder Gruppe, die für die Sammlung verantwortlich ist. Normalerweise ist dies der Autor des Inhalts. Persönliche Namen sollten mit dem Nachnamen zuerst eingegeben werden, z. "Smith, John". date_created: Das Datum, an dem die Sammlung erstellt wurde. @@ -1125,7 +1126,7 @@ de: share_applies_to_new_works: Wenn neue Werke direkt in der Sammlung erstellt werden, erteilen Sie Benutzern und Gruppen Berechtigungen für das neue Werk entsprechend ihrer Sammlungsrollen. title: '' defaults: - based_near: Ein Ortsname, der sich auf das Werk bezieht, z. B. der Veröffentlichungsort oder die Stadt, das Bundesland oder das Land, um das es in dem Arbeitsinhalt geht. Ruft den GeoNames-Webdienst auf. + based_near: Ein Ortsname, der sich auf das Werk bezieht, z. B. der Ort der Veröffentlichung oder die Stadt, das Bundesland oder das Land, um das es in den Inhalten des Werks geht. Ruft die Geonames-Webdienstseite.
    2. Gehen Sie zu Ihren Geonames-Kontoeinstellungen und wählen Sie unten die Option aus, um kostenlose Dienste zu aktivieren.
    3. Gehen Sie im Dashboard
    zu den Einstellungen Ihres Hyku-Kontos. Geben Sie Ihren Geonames-Benutzernamen in den Hyrax-Kontoeinstellungen ein.
    5. Sie sollten nun die Liste der Standorte in diesem Dropdown-Menü sehen können. contributor: Eine Person oder Gruppe, die Sie als eine Rolle bei der Erstellung der Arbeit erkennen möchten, jedoch nicht die primäre Rolle. creator: Die Person oder Gruppe, die für die Arbeit verantwortlich ist. Normalerweise ist dies der Autor des Inhalts. Persönliche Namen sollten mit dem Nachnamen zuerst eingegeben werden, z. "Smith, John". date_created: Das Datum, an dem die Arbeit erstellt wurde. @@ -1140,6 +1141,7 @@ de: resource_type: Vordefinierte Kategorien zur Beschreibung der Art des hochgeladenen Inhalts, z. B. "Artikel". oder "Datensatz". Es kann mehr als ein Typ ausgewählt werden. subject: Überschriften oder Indexbegriffe, die beschreiben, worum es in der Arbeit geht; Diese müssen einem vorhandenen Wortschatz entsprechen. title: Ein Name, der bei der Identifizierung eines Werks hilft. + video_embed: Wenn Sie einen Einbettungslink für ein Video eingeben, muss es sich um eine ordnungsgemäß formatierte URL handeln, die mit „http://“ oder „https://“ beginnt. Es muss außerdem einen gültigen Link zu einem gehosteten Video enthalten, das in einem Iframe angezeigt werden kann.

    Beispiele:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: Umfang labels: @@ -1162,12 +1164,13 @@ de: defaults: admin_set_id: Verwaltungsset based_near: Ort + contributor: Mitwirkender creator: Schöpfer date_created: Datum erstellt default_button_background_color: Standardhintergrundfarbe der Schaltfläche default_button_border_color: Standardfarbe des Schaltflächenrahmens default_button_text_color: Standardfarbe für den Schaltflächentext - description: Beschreibung + description: Zusammenfassung oder Zusammenfassung embargo_release_date: bis um facet_panel_background_color: Hintergrundfarbe des Facettenfelds facet_panel_text_color: Textfarbe des Facettenbereichs @@ -1176,6 +1179,7 @@ de: footer_link_hover_color: Hover-Farbe des Fußzeilen-Links header_and_footer_background_color: Hintergrundfarbe für Kopf- und Fußzeile header_and_footer_text_color: Textfarbe für Kopf- und Fußzeile + identifier: Kennung keyword: Stichwort lease_expiration_date: bis um license: Lizenz @@ -1187,8 +1191,10 @@ de: navbar_link_text_color: Textfarbe für Navigationsleisten-Links navbar_link_text_hover_color: Hover-Farbe des Navigationsleisten-Linktextes primary_button_hover_color: Hover-Farbe der primären Schaltfläche + publisher: Verleger related_url: Verwandte URL rights_statement: Rechteerklärung + source: Quelle title: Titel visibility_after_embargo: dann öffne es bis visibility_after_lease: dann beschränken Sie es auf diff --git a/config/locales/hyrax.en.yml b/config/locales/hyrax.en.yml index 9f703862a..9776e7e98 100644 --- a/config/locales/hyrax.en.yml +++ b/config/locales/hyrax.en.yml @@ -253,8 +253,9 @@ en: one: There is %{count} user in this repository. other: There are %{count} users in this repository. id_label: Username - role_label: Roles group_label: Groups + group_role_label: Group roles + site_role_label: Site roles title: Manage Users reader_title: View Users workflow_roles: @@ -436,7 +437,7 @@ en: select_label: Select subcollection nested_subcollection: button_label: Add to collection - desc: Add existing collections to this collection + desc: Add this collection to another collection modal_title: Add this Collection Within Another Collection select_label: Select collection browse_view: Browse View @@ -710,7 +711,6 @@ en: shared: Works Shared with Me sr: batch_checkbox: Check to add to a collection or edit list - collections_batch_checkbox: Check to batch delete collections. check_all_label: Select all files to be added to a collection or edited detail_label: Display summary details of listing: Listing of items you have deposited in @@ -1104,7 +1104,8 @@ en: description: A brief overarching description that applies to all works collected in this set. For example, "Theses and supplementary files created by the School of Earth Sciences graduate students." title: A name to aid in identifying the Administrative Set and to distinguish it from other Administrative Sets in the repository. collection: - based_near: A place name related to the collection, such as its site of publication, or the city, state, or country the collection contents are about. Calls upon the GeoNames web service. + based_near: "A place name related to the work, such as its site of publication, or the city, state, or country the work contents are about. Calls upon the GeoNames web service. Please note, you must configure your geonames username in the account settings, or this field will have a loading error.
    +
    Steps to enable this field:
    1. Create a free account on the Geonames web service page.
    2. Go to your geonames account settings and select the option at the bottom to enable free services.
    3. Go into your hyku account settings in the dashboard.
    4. Enter your geonames username in the hyrax account settings.
    5. You should now be able to see the list of locations in this dropdown." contributor: A person or group you want to recognize for playing a role in the creation of the collection, but not the primary role. creator: The person or group responsible for the collection. Usually this is the author of the content. Personal names should be entered with the last name first, e.g. "Smith, John.". date_created: The date on which the collection was created. @@ -1131,10 +1132,11 @@ en: share_applies_to_new_works: When new works are created directly in the collection, grant sharing users and groups permissions for the new work according to their collection roles. title: "" defaults: - based_near: A place name related to the work, such as its site of publication, or the city, state, or country the work contents are about. Calls upon the GeoNames web service. + based_near: "A place name related to the work, such as its site of publication, or the city, state, or country the work contents are about. Calls upon the GeoNames web service. Please note, you must configure your geonames username in the account settings, or this field will have a loading error.
    +
    Steps to enable this field:
    1. Create a free account on the Geonames web service page.
    2. Go to your geonames account settings and select the option at the bottom to enable free services.
    3. Go into your hyku account settings in the dashboard.
    4. Enter your geonames username in the hyrax account settings.
    5. You should now be able to see the list of locations in this dropdown." contributor: A person or group you want to recognize for playing a role in the creation of the work, but not the primary role. creator: The person or group responsible for the work. Usually this is the author of the content. Personal names should be entered with the last name first, e.g. "Smith, John.". - date_created: The date on which the work was created. + date_created: The date on which the work was created. Must adhere to either the format YYYY, YYYY-MM, or YYYY-MM-DD in order to be filtered and sorted. description: Free-text notes about the work. Examples include abstracts of a paper or citation information for a journal article. extent: The extent (size, duration, number, etc.) of the work. identifier: A unique handle identifying the work. An example would be a DOI for a journal article, or an ISBN or OCLC number for a book. @@ -1146,6 +1148,7 @@ en: resource_type: Pre-defined categories to describe the type of content being uploaded, such as "article" or "dataset." More than one type may be selected. subject: Headings or index terms describing what the work is about; these do need to conform to an existing vocabulary. title: A name to aid in identifying a work. + video_embed: "If you enter an embed link for a video, it must be a properly formatted url beginning with 'http://' or 'https://'. It also needs to contain a valid link to a hosted video that can appear in an iframe.

    Examples:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/Znf73dsFdC8
    " labels: collection: size: Size @@ -1166,6 +1169,7 @@ en: defaults: admin_set_id: Administrative Set based_near: Location + contributor: Contributor creator: Creator date_created: Date Created default_button_background_color: Default button background color @@ -1180,6 +1184,7 @@ en: footer_link_hover_color: Footer link hover color header_and_footer_background_color: Header and footer background color header_and_footer_text_color: Header and footer text color + identifier: Identifier keyword: Keyword lease_expiration_date: until license: License @@ -1191,8 +1196,10 @@ en: navbar_link_text_color: Navbar link text color navbar_link_text_hover_color: Navbar link text hover color primary_button_hover_color: Primary button hover color + publisher: Publisher related_url: Related URL rights_statement: Rights statement + source: Source title: Title visibility_after_embargo: then open it up to visibility_after_lease: then restrict it to diff --git a/config/locales/hyrax.es.yml b/config/locales/hyrax.es.yml index 88722366c..c1135cb99 100644 --- a/config/locales/hyrax.es.yml +++ b/config/locales/hyrax.es.yml @@ -244,9 +244,11 @@ es: one: Hay usuarios %{count} en este repositorio. other: Hay usuarios de %{count} en este repositorio. group_label: Grupos + group_role_label: Roles grupales id_label: Nombre de usuario reader_title: Ver usuarios role_label: Roles + site_role_label: Roles del sitio title: Administrar usuarios workflow_roles: header: Roles de flujo de trabajo @@ -296,7 +298,7 @@ es: background_attribution_html: '' base: citations: - header: 'Citación' + header: Citación form_child_work_relationships: actions: remove: Eliminar de este trabajo @@ -697,7 +699,6 @@ es: sr: batch_checkbox: Marque para agregar a una colección o editar lista check_all_label: Seleccione todos los archivos para agregar a una colección o editar - collections_batch_checkbox: Marque para eliminar colecciones por lotes. detail_label: Mostrar detalles de resumen de listing: Listado de artículos que ha depositado en press_to: Presione para @@ -1099,7 +1100,7 @@ es: description: Una breve descripción general que se aplica a todos los trabajos recopilados en este conjunto. Por ejemplo, "Tesis y archivos complementarios creados por los estudiantes graduados de la Facultad de Ciencias de la Tierra". title: Un nombre para ayudar a identificar el conjunto administrativo y distinguirlo de otros conjuntos administrativos en el repositorio. collection: - based_near: Un nombre de lugar relacionado con la colección, como su sitio de publicación, o la ciudad, el estado o el país del que trata el contenido de la colección. Llama al servicio web GeoNames . + based_near: El nombre de un lugar relacionado con la obra, como su sitio de publicación, o la ciudad, el estado o el país sobre el que trata el contenido de la obra. Llama a Servicio web de GeoNames. Tenga en cuenta que debe configurar su nombre de usuario de geonames en la configuración de la cuenta, o este campo tendrá un error de carga.

    Pasos para habilitar este campo:
    1. Cree una cuenta gratuita en la página del servicio web de Geonames.
    2. Vaya a su configuración de cuenta de geonames y seleccione la opción en la parte inferior para habilitar los servicios gratuitos.
    3. Vaya a la configuración de su cuenta hyku en el panel de control.
    4. Ingrese su nombre de usuario de geonames en la configuración de la cuenta hyrax.
    5. Ahora debería poder ver la lista de ubicaciones en este menú desplegable contributor: Una persona o grupo que desea reconocer por desempeñar un papel en la creación de la colección, pero no el rol principal. creator: La persona o grupo responsable de la colección. Por lo general, este es el autor del contenido. Los nombres personales deben ingresarse primero con el apellido, p. & quot; Smith, John & quot ;. date_created: La fecha en que se creó la colección. @@ -1126,7 +1127,7 @@ es: share_applies_to_new_works: Cuando se crean nuevos trabajos directamente en la colección, otorgue permisos para compartir usuarios y grupos para el nuevo trabajo de acuerdo con sus roles de colección. title: '' defaults: - based_near: Un nombre de lugar relacionado con el trabajo, como su sitio de publicación, o la ciudad, el estado o el país del que trata el contenido del trabajo. Llama al servicio web GeoNames . + based_near: El nombre de un lugar relacionado con la obra, como su sitio de publicación, o la ciudad, el estado o el país sobre el que trata el contenido de la obra. Llama a Servicio web de GeoNames. Tenga en cuenta que debe configurar su nombre de usuario de geonames en la configuración de la cuenta, o este campo tendrá un error de carga.

    Pasos para habilitar este campo:
    1. Cree una cuenta gratuita en la página del servicio web de Geonames.
    2. Vaya a su configuración de cuenta de geonames y seleccione la opción en la parte inferior para habilitar los servicios gratuitos.
    3. Vaya a la configuración de su cuenta hyku en el panel de control.
    4. Ingrese su nombre de usuario de geonames en la configuración de la cuenta hyrax.
    5. Ahora debería poder ver la lista de ubicaciones en este menú desplegable. contributor: Una persona o grupo que desea reconocer por desempeñar un papel en la creación del trabajo, pero no el papel principal. creator: La persona o grupo responsable del trabajo. Por lo general, este es el autor del contenido. Los nombres personales deben ingresarse primero con el apellido, p. & quot; Smith, John & quot ;. date_created: La fecha en que se creó el trabajo. @@ -1141,6 +1142,7 @@ es: resource_type: Categorías predefinidas para describir el tipo de contenido que se está cargando, como "artículo". o "conjunto de datos". Se puede seleccionar más de un tipo. subject: Encabezados o términos de índice que describen de qué trata el trabajo; estos deben ajustarse a un vocabulario existente. title: Un nombre para ayudar a identificar una obra. + video_embed: Si ingresa un enlace para insertar para un video, debe ser una URL con el formato adecuado que comience con 'http://' o 'https://'. También debe contener un enlace válido a un vídeo alojado que pueda aparecer en un iframe.

    Ejemplos:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: Extensión labels: @@ -1163,12 +1165,13 @@ es: defaults: admin_set_id: Conjunto administrativo based_near: Ubicación + contributor: Contribuyente creator: Creador date_created: fecha de creacion default_button_background_color: Color de fondo predeterminado del botón default_button_border_color: Color predeterminado del borde del botón default_button_text_color: Color de texto de botón predeterminado - description: Descripción + description: Resumen o resumen embargo_release_date: hasta facet_panel_background_color: Color de fondo del panel de facetas facet_panel_text_color: Color del texto del panel de facetas @@ -1177,6 +1180,7 @@ es: footer_link_hover_color: Color de desplazamiento del enlace de pie de página header_and_footer_background_color: Color de fondo de encabezado y pie de página header_and_footer_text_color: Color de texto de encabezado y pie de página + identifier: Identificador keyword: Palabra clave lease_expiration_date: hasta license: Licencia @@ -1188,8 +1192,10 @@ es: navbar_link_text_color: Color del texto del enlace de la barra de navegación navbar_link_text_hover_color: Color de desplazamiento del texto del vínculo de la barra de navegación primary_button_hover_color: Color de desplazamiento del botón principal + publisher: Editor related_url: URL relacionada rights_statement: Declaración de derechos + source: Fuente title: Título visibility_after_embargo: luego ábralo para visibility_after_lease: luego restringirlo a diff --git a/config/locales/hyrax.fr.yml b/config/locales/hyrax.fr.yml index 15557a4a7..8974552ba 100644 --- a/config/locales/hyrax.fr.yml +++ b/config/locales/hyrax.fr.yml @@ -242,9 +242,11 @@ fr: one: Il y a un utilisateur %{count} dans ce référentiel. other: Il y a des utilisateurs %{count} dans ce référentiel. group_label: Groupes + group_role_label: Rôles de groupe id_label: Nom d'utilisateur reader_title: Afficher les utilisateurs role_label: Les rôles + site_role_label: Rôles sur le site title: gérer les utilisateurs workflow_roles: header: Rôles de workflow @@ -294,7 +296,7 @@ fr: background_attribution_html: '' base: citations: - header: 'Citation' + header: Citation form_child_work_relationships: actions: remove: Supprimer de ce travail @@ -695,7 +697,6 @@ fr: sr: batch_checkbox: Cocher pour ajouter à une collection ou modifier la liste check_all_label: Sélectionnez tous les fichiers à ajouter à une collection ou à modifier - collections_batch_checkbox: Cochez pour supprimer les collections par lots. detail_label: Afficher les détails du résumé de listing: Liste des articles dans lesquels vous avez déposé press_to: Appuyez sur pour @@ -1139,6 +1140,7 @@ fr: resource_type: Catégories prédéfinies pour décrire le type de contenu en cours de téléchargement, telles que & quot; article & quot; ou & quot; ensemble de données. & quot; Plusieurs types peuvent être sélectionnés. subject: En-têtes ou termes d'index décrivant l'objet du travail; ceux-ci doivent se conformer à un vocabulaire existant. title: Un nom pour aider à identifier une œuvre. + video_embed: Si vous saisissez un lien d'intégration pour une vidéo, il doit s'agir d'une URL correctement formatée commençant par « http:// » ou « https:// ». Il doit également contenir un lien valide vers une vidéo hébergée pouvant apparaître dans une iframe.

    Exemples :
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: Ampleur labels: @@ -1175,6 +1177,7 @@ fr: footer_link_hover_color: Couleur de survol du lien de pied de page header_and_footer_background_color: Couleur d'arrière-plan de l'en-tête et du pied de page header_and_footer_text_color: Couleur du texte de l'en-tête et du pied de page + identifier: Identifiant keyword: Mot-clé lease_expiration_date: jusqu'à ce que license: Licence @@ -1186,8 +1189,10 @@ fr: navbar_link_text_color: Couleur du texte du lien de la barre de navigation navbar_link_text_hover_color: Couleur de survol du texte du lien de la barre de navigation primary_button_hover_color: Couleur de survol du bouton principal + publisher: Éditeur related_url: URL associée rights_statement: Déclaration des droits + source: La source title: Titre visibility_after_embargo: puis ouvrez-le pour visibility_after_lease: puis limitez-le à diff --git a/config/locales/hyrax.it.yml b/config/locales/hyrax.it.yml index 2a941f62c..498843e85 100644 --- a/config/locales/hyrax.it.yml +++ b/config/locales/hyrax.it.yml @@ -244,9 +244,11 @@ it: one: C'è un utente %{count} in questo repository. other: Ci sono utenti %{count} in questo repository. group_label: Gruppi + group_role_label: Ruoli del gruppo id_label: Nome utente reader_title: Visualizza utenti role_label: ruoli + site_role_label: Ruoli del sito title: Gestisci utenti workflow_roles: header: Ruoli del flusso di lavoro @@ -296,7 +298,7 @@ it: background_attribution_html: '' base: citations: - header: 'Citazioni' + header: Citazioni form_child_work_relationships: actions: remove: Rimuovi da questo lavoro @@ -697,7 +699,6 @@ it: sr: batch_checkbox: Seleziona per aggiungere a una raccolta o modificare l'elenco check_all_label: Seleziona tutti i file da aggiungere a una raccolta o modificati - collections_batch_checkbox: Selezionare per eliminare in batch le raccolte. detail_label: Visualizza i dettagli di riepilogo di listing: Elenco degli oggetti in cui hai depositato press_to: Premere per @@ -1099,7 +1100,7 @@ it: description: Una breve descrizione generale che si applica a tutte le opere raccolte in questo set. Ad esempio, "Tesi e file supplementari creati dagli studenti laureati della School of Earth Sciences". title: Un nome per facilitare l'identificazione del set amministrativo e per distinguerlo dagli altri set amministrativi nel repository. collection: - based_near: Un nome di luogo correlato alla raccolta, ad esempio il sito di pubblicazione o la città, lo stato o il paese di cui sono contenuti i contenuti della raccolta. Invita il servizio web GeoNames . + based_near: Un nome di luogo correlato all'opera, ad esempio il sito di pubblicazione o la città, lo stato o il paese di cui tratta il contenuto dell'opera. Invita Servizio web GeoNames. Tieni presente che devi configurare il tuo nome utente GeoNames nelle impostazioni dell'account, altrimenti questo campo avrà un errore di caricamento.

    Passaggi per abilitare questo campo:
    1. Crea un account gratuito sulla pagina del servizio web Geonames.
    2. Vai alle impostazioni dell'account geonames e seleziona l'opzione in basso per abilitare i servizi gratuiti.
    3. Accedi alle impostazioni del tuo account hyku nella dashboard.
    4. Inserisci il tuo nome utente geonames nelle impostazioni dell'account hyrax.
    5. Ora dovresti essere in grado di visualizzare l'elenco delle località in questo menu a discesa contributor: Una persona o un gruppo che desideri riconoscere per avere un ruolo nella creazione della collezione, ma non il ruolo principale. creator: La persona o il gruppo responsabile della raccolta. Di solito questo è l'autore del contenuto. I nomi personali devono essere inseriti prima con il cognome, ad es. "Smith, John." date_created: La data in cui è stata creata la raccolta. @@ -1126,7 +1127,7 @@ it: share_applies_to_new_works: Quando le nuove opere vengono create direttamente nella raccolta, concedere le autorizzazioni di condivisione di utenti e gruppi per la nuova opera in base ai ruoli della raccolta. title: '' defaults: - based_near: Un nome di luogo correlato all'opera, ad esempio il suo sito di pubblicazione, o la città, lo stato o il paese di cui si tratta. Invita il servizio web GeoNames . + based_near: Un nome di luogo correlato all'opera, ad esempio il sito di pubblicazione o la città, lo stato o il paese di cui tratta il contenuto dell'opera. Invita Servizio web GeoNames. Tieni presente che devi configurare il tuo nome utente GeoNames nelle impostazioni dell'account, altrimenti questo campo avrà un errore di caricamento.

    Passaggi per abilitare questo campo:
    1. Crea un account gratuito sulla pagina del servizio web Geonames.
    2. Vai alle impostazioni dell'account geonames e seleziona l'opzione in basso per abilitare i servizi gratuiti.
    3. Accedi alle impostazioni del tuo account hyku nella dashboard.
    4. Inserisci il tuo nome utente geonames nelle impostazioni dell'account hyrax.
    5. Ora dovresti essere in grado di visualizzare l'elenco delle località in questo menu a discesa contributor: Una persona o un gruppo che vuoi riconoscere per avere un ruolo nella creazione dell'opera, ma non il ruolo principale. creator: La persona o il gruppo responsabile del lavoro. Di solito questo è l'autore del contenuto. I nomi personali devono essere inseriti prima con il cognome, ad es. "Smith, John." date_created: La data di creazione dell'opera. @@ -1141,6 +1142,7 @@ it: resource_type: Categorie predefinite per descrivere il tipo di contenuto da caricare, come "articolo" o & quot; set di dati. & quot; È possibile selezionare più di un tipo. subject: Intestazioni o termini indicativi che descrivono di cosa tratta il lavoro; questi devono conformarsi a un vocabolario esistente. title: Un nome per aiutare a identificare un'opera. + video_embed: Se inserisci un link per incorporare un video, deve essere un URL formattato correttamente che inizia con "http://" o "https://". Deve inoltre contenere un collegamento valido a un video ospitato che può essere visualizzato in un iframe.

    Esempi:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: Estensione labels: @@ -1177,6 +1179,7 @@ it: footer_link_hover_color: Colore al passaggio del mouse del collegamento a piè di pagina header_and_footer_background_color: Colore di sfondo dell'intestazione e del piè di pagina header_and_footer_text_color: Colore del testo dell'intestazione e del piè di pagina + identifier: Identificatore keyword: Parola chiave lease_expiration_date: fino a license: Licenza @@ -1188,8 +1191,10 @@ it: navbar_link_text_color: Colore del testo del collegamento alla barra di navigazione navbar_link_text_hover_color: Colore al passaggio del mouse del testo del collegamento della barra di navigazione primary_button_hover_color: Colore del pulsante principale al passaggio del mouse + publisher: Editore related_url: URL correlato rights_statement: Dichiarazione dei diritti + source: fonte title: Titolo visibility_after_embargo: quindi aprilo fino a visibility_after_lease: quindi limitalo a diff --git a/config/locales/hyrax.pt-BR.yml b/config/locales/hyrax.pt-BR.yml index a97a1025f..f07f8f96a 100644 --- a/config/locales/hyrax.pt-BR.yml +++ b/config/locales/hyrax.pt-BR.yml @@ -244,9 +244,11 @@ pt-BR: one: Há um usuário %{count} neste repositório. other: Existem usuários %{count} neste repositório. group_label: Grupos + group_role_label: Funções do grupo id_label: Nome do usuário reader_title: Ver usuários role_label: Funções + site_role_label: Funções do site title: Gerenciar usuários workflow_roles: header: Funções de fluxo de trabalho @@ -697,7 +699,6 @@ pt-BR: sr: batch_checkbox: Marque para adicionar a uma coleção ou editar uma lista check_all_label: Selecione todos os arquivos a serem adicionados a uma coleção ou editados - collections_batch_checkbox: Marque para excluir coleções em lote. detail_label: Exibir detalhes resumidos de listing: Lista de itens nos quais você depositou press_to: Pressione para @@ -1099,7 +1100,7 @@ pt-BR: description: Uma breve descrição abrangente que se aplica a todos os trabalhos coletados neste conjunto. Por exemplo, "Teses e arquivos complementares criados pelos alunos de graduação da Escola de Ciências da Terra". title: Um nome para ajudar na identificação do Conjunto Administrativo e para distingui-lo de outros Conjuntos Administrativos no repositório. collection: - based_near: Um nome de local relacionado à coleção, como o site da publicação ou a cidade, estado ou país em que o conteúdo da coleção se refere. Solicita o serviço da web GeoNames . + based_near: Um nome de local relacionado ao trabalho, como seu site de publicação, ou a cidade, estado ou país sobre o qual o conteúdo do trabalho se refere. Solicita Serviço da web GeoNames. Observe que você deve configurar seu nome de usuário geonames nas configurações da conta, ou este campo terá um erro de carregamento.

    Passos para habilitar este campo:
    1. Crie uma conta gratuita na página do serviço web Geonames.
    2. Acesse suas configurações de conta de geonames e selecione a opção na parte inferior para habilitar serviços gratuitos.
    3. Acesse as configurações da sua conta hyku no painel.
    4. Digite seu nome de usuário geonames nas configurações da conta hyrax.
    5. Agora você deve conseguir ver a lista de locais neste menu suspenso contributor: Uma pessoa ou grupo que você deseja reconhecer por desempenhar um papel na criação da coleção, mas não o papel principal. creator: A pessoa ou grupo responsável pela coleção. Normalmente, este é o autor do conteúdo. Os nomes pessoais devem ser inseridos com o sobrenome primeiro, p. "Smith, John". date_created: A data em que a coleção foi criada. @@ -1126,7 +1127,7 @@ pt-BR: share_applies_to_new_works: Quando novos trabalhos são criados diretamente na coleção, conceda aos usuários e grupos de compartilhamento permissões para o novo trabalho, de acordo com suas funções de coleção. title: '' defaults: - based_near: Um nome de local relacionado ao trabalho, como o site da publicação ou a cidade, estado ou país em que o conteúdo do trabalho se refere. Solicita o serviço da web GeoNames . + based_near: Um nome de local relacionado ao trabalho, como seu site de publicação, ou a cidade, estado ou país sobre o qual o conteúdo do trabalho se refere. Solicita Serviço da web GeoNames. Observe que você deve configurar seu nome de usuário geonames nas configurações da conta, ou este campo terá um erro de carregamento.

    Passos para habilitar este campo:
    1. Crie uma conta gratuita na página do serviço web Geonames.
    2. Acesse suas configurações de conta de geonames e selecione a opção na parte inferior para habilitar serviços gratuitos.
    3. Acesse as configurações da sua conta hyku no painel.
    4. Digite seu nome de usuário geonames nas configurações da conta hyrax.
    5. Agora você deve conseguir ver a lista de locais neste menu suspenso contributor: Uma pessoa ou grupo que você deseja reconhecer por desempenhar um papel na criação do trabalho, mas não o papel principal. creator: A pessoa ou grupo responsável pelo trabalho. Normalmente, este é o autor do conteúdo. Os nomes pessoais devem ser inseridos com o sobrenome primeiro, p. "Smith, John". date_created: A data em que o trabalho foi criado. @@ -1141,6 +1142,7 @@ pt-BR: resource_type: Categorias predefinidas para descrever o tipo de conteúdo que está sendo carregado, como "artigo" ou "conjunto de dados". Mais de um tipo pode ser selecionado. subject: Cabeçalhos ou termos do índice que descrevem o que é o trabalho; estes precisam estar em conformidade com um vocabulário existente. title: Um nome para ajudar na identificação de um trabalho. + video_embed: Se você inserir um link incorporado para um vídeo, ele deverá ser um URL formatado corretamente, começando com 'http://' ou 'https://'. Ele também precisa conter um link válido para um vídeo hospedado que possa aparecer em um iframe.

    Exemplos:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: Extensão labels: @@ -1177,6 +1179,7 @@ pt-BR: footer_link_hover_color: Cor do foco do link do rodapé header_and_footer_background_color: Cor de fundo do cabeçalho e rodapé header_and_footer_text_color: Cor do texto do cabeçalho e rodapé + identifier: Identificador keyword: Palavra-chave lease_expiration_date: até license: Licença diff --git a/config/locales/hyrax.zh.yml b/config/locales/hyrax.zh.yml index 6c2732bf0..7260273a7 100644 --- a/config/locales/hyrax.zh.yml +++ b/config/locales/hyrax.zh.yml @@ -244,9 +244,11 @@ zh: one: 该存储库中有%{count}个用户 。 other: 此存储库中有%{count}个用户 。 group_label: 团体 + group_role_label: 团体角色 id_label: 用户名 reader_title: 查看用户 role_label: 的角色 + site_role_label: 站点角色 title: 管理用户 workflow_roles: header: 工作流程角色 @@ -697,7 +699,6 @@ zh: sr: batch_checkbox: 检查添加到收藏夹或编辑列表 check_all_label: 选择所有要添加到集合或编辑的文件 - collections_batch_checkbox: 勾选批量删除集合。 detail_label: 显示的摘要详细信息 listing: 您存放的物品清单 press_to: 按到 @@ -1099,7 +1100,7 @@ zh: description: 简短的总体描述,适用于本系列中收集的所有作品。例如,“地球科学学院研究生创建的论文和补充文件”。 title: 一个名称,以帮助标识管理集并将其与存储库中的其他管理集区分开。 collection: - based_near: 与馆藏相关的地名,例如馆藏的出版地点,馆藏内容所在的城市,州或国家。调用 GeoNames Web服务。 + based_near: 与作品相关的地名,例如其出版地点,或作品内容所涉及的城市、州或国家。调用 GeoNames Web 服务。请注意,您必须在帐户设置中配置您的 geonames 用户名,否则此字段将出现加载错误。

    启用此字段的步骤:
    1.在 Geonames 网络服务页面
    创建一个免费帐户。2.转到您的 geonames 帐户设置,然后选择底部的选项以启用免费服务。
    3.在仪表板中进入您的 hyku 帐户设置。
    4.在 hyrax 帐户设置中输入您的 geonames 用户名。
    5.您现在应该能够在此下拉列表中看到位置列表 contributor: 您想通过在集合创建过程中扮演的角色而被认可的个人或团体,而不是主要角色。 creator: 负责收集的个人或团体。通常,这是内容的作者。输入的个人名字应使用姓氏,例如“约翰·史密斯”。 date_created: 集合创建的日期。 @@ -1126,7 +1127,7 @@ zh: share_applies_to_new_works: 在集合中直接创建新作品时,请根据其收藏角色授予共享用户和组新作品的权限。 title: '' defaults: - based_near: 与该作品相关的地名,例如其出版地或该作品所涉及的城市,州或国家。调用 GeoNames Web服务。 + based_near: 与作品相关的地名,例如其出版地点,或作品内容所涉及的城市、州或国家。调用 GeoNames Web 服务。请注意,您必须在帐户设置中配置您的 geonames 用户名,否则此字段将出现加载错误。

    启用此字段的步骤:
    1.在 Geonames 网络服务页面
    创建一个免费帐户。2.转到您的 geonames 帐户设置,然后选择底部的选项以启用免费服务。
    3.在仪表板中进入您的 hyku 帐户设置。
    4.在 hyrax 帐户设置中输入您的 geonames 用户名。
    5.您现在应该能够在此下拉列表中看到位置列表 contributor: 您希望因在作品创作中扮演的角色而被认可的个人或团体,而不是主要角色。 creator: 负责工作的个人或团体。通常,这是内容的作者。输入的个人名字应使用姓氏,例如“约翰·史密斯”。 date_created: 工作创建的日期。 @@ -1141,6 +1142,7 @@ zh: resource_type: 用于描述要上载的内容类型的预定义类别,例如“商品”,“商品”,“商品”,或“数据集”。可以选择一种以上的类型。 subject: 描述工作内容的标题或索引词;这些确实需要符合现有词汇。 title: 有助于识别作品的名称。 + video_embed: 如果您输入视频的嵌入链接,它必须是格式正确、以“http://”或“https://”开头的网址。它还需要包含指向可显示在 iframe 中的托管视频的有效链接。

    示例:
    https://player.vimeo.com/video/467264493?h=b089de0eab
    https://www.youtube.com/embed/ Znf73dsFdC8
    labels: extent: 程度 labels: @@ -1177,6 +1179,7 @@ zh: footer_link_hover_color: 页脚链接悬停颜色 header_and_footer_background_color: 页眉和页脚背景颜色 header_and_footer_text_color: 页眉和页脚文本颜色 + identifier: 识别码 keyword: 关键词 lease_expiration_date: 直到 license: 执照 @@ -1188,8 +1191,10 @@ zh: navbar_link_text_color: 导航栏链接文字颜色 navbar_link_text_hover_color: 导航栏链接文本悬停颜色 primary_button_hover_color: 主按钮悬停颜色 + publisher: 发行人 related_url: 相关网址 rights_statement: 权利声明 + source: 资源 title: 标题 visibility_after_embargo: 然后打开它 visibility_after_lease: 然后将其限制为 diff --git a/config/locales/it.yml b/config/locales/it.yml index 16ec91396..3cae90ac3 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1,5 +1,12 @@ --- it: + errors: + messages: + valid_embed_url: "deve essere un URL di incorporamento valido di YouTube o Vimeo." + activefedora: + models: + generic_work: Lavoro + image: Immagine activerecord: attributes: site: @@ -7,6 +14,8 @@ it: application: tagline: La soluzione di repository di nuova generazione helpers: + action: + become: Diventare submit: add_role_to_group: submit: Inserisci @@ -163,6 +172,8 @@ it: forms: banner_image: hint: Per utilizzare un'immagine come sfondo di masthead, è necessario utilizzare un'immagine (JPG, GIF o PNG) alta almeno 120 pixel e larga 1200 pixel. Per risultati ottimali, utilizzare un'immagine larga almeno 1800 pixel. + collection_banner_text_color: + hint: Il colore del testo (titolo e data dell'ultimo aggiornamento) all'interno del banner della raccolta. custom_css: confirm: I CSS personalizzati sostituiranno sempre altre selezioni di temi. Procedere? warning: Se le personalizzazioni dei temi non sembrano essere applicate correttamente, verifica che non vengano sovrascritte dal CSS personalizzato. @@ -172,7 +183,7 @@ it: alert: Carica almeno un file prima di inviarlo. hint: Per le immagini predefinite, è necessario utilizzare un'immagine (JPG, GIF o PNG) che abbia le stesse dimensioni in altezza e larghezza (100 pixel in larghezza e 100 pixel in altezza) directory_image: - hint: Per utilizzare un'immagine come immagine della directory, devi utilizzare un'immagine (JPG, GIF o PNG) non più alta dell'intestazione e non più larga di 400 pixel. + hint: L'immagine della directory deve utilizzare un'immagine (JPG, GIF o PNG). La larghezza dell'immagine non dovrebbe essere più ampia di 2 volte l'altezza. facet_panel_background_color: hint: Si applica ai facet e alle intestazioni di sezione aggiuntive nelle pagine di lavoro in alcuni temi. facet_panel_text_color: @@ -209,12 +220,10 @@ it: fonts: Font themes: Temi sidebar: - account: Account accounts: conti activity_summary: Riepilogo attività labels: etichette manage_groups: Gestisci gruppi - repository_activity: Attività di deposito system_status: Stato del sistema users: activate: @@ -234,6 +243,9 @@ it: active: Attivo pending: in attesa di status_label: Stato + roles: + remove: + confirmation: Sei sicuro di voler rimuovere il ruolo "%{role}" dall'utente "%{user}"? permissions: collections: cannot: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index f4da8af58..117fce043 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1,5 +1,12 @@ --- pt-BR: + errors: + messages: + valid_embed_url: "deve ser uma URL de incorporação válida do YouTube ou Vimeo." + activefedora: + models: + generic_work: Trabalhar + image: Imagem activerecord: attributes: site: @@ -7,6 +14,8 @@ pt-BR: application: tagline: A solução de repos helpers: + action: + become: Tornar-se submit: add_role_to_group: submit: Adicionar @@ -163,6 +172,8 @@ pt-BR: forms: banner_image: hint: Para usar uma imagem como plano de fundo do cabeçalho, você deve usar uma imagem (JPG, GIF ou PNG) com pelo menos 120 pixels de altura e 1200 pixels de largura. Para obter melhores resultados, use uma imagem com pelo menos 1800 pixels de largura. + collection_banner_text_color: + hint: A cor do texto (título e data da última atualização) dentro do banner da coleção. custom_css: confirm: CSS personalizado sempre substituirá outras seleções de temas. Continuar? warning: Se suas personalizações de temas não parecem estar sendo aplicadas corretamente, verifique se elas não estão sendo substituídas pelo CSS personalizado. @@ -172,7 +183,7 @@ pt-BR: alert: Faça upload de pelo menos um arquivo antes de enviar. hint: Para imagens padrão, você deve usar uma imagem (JPG, GIF ou PNG) que tenha dimensões iguais de altura e largura (100 pixels de largura e 100 pixels de altura) directory_image: - hint: Para usar uma imagem como imagem do diretório, você deve usar uma imagem (JPG, GIF ou PNG) que não seja mais alta que o cabeçalho e não tenha mais de 400 pixels de largura. + hint: A imagem do diretório deve usar uma imagem (JPG, GIF ou PNG). A largura da imagem não deve ser maior que 2x a altura. facet_panel_background_color: hint: Aplica-se a facetas e cabeçalhos de seção adicionais nas páginas de trabalho em alguns temas. facet_panel_text_color: @@ -209,12 +220,10 @@ pt-BR: fonts: Fontes themes: Temas sidebar: - account: Conta accounts: Contas activity_summary: Resumo da atividade labels: Etiquetas manage_groups: Gerenciar grupos - repository_activity: Atividade do repositório system_status: Status do sistema users: activate: @@ -234,6 +243,9 @@ pt-BR: active: Ativo pending: Pendente status_label: Status + roles: + remove: + confirmation: Tem certeza de que deseja remover a função "%{role}" do usuário "%{user}"? permissions: collections: cannot: diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml index 348096016..2591a0b82 100644 --- a/config/locales/simple_form.de.yml +++ b/config/locales/simple_form.de.yml @@ -7,55 +7,34 @@ de: hints: account: admin_emails: Geben Sie jeweils eine E-Mail-Adresse ein - allow_signup: Erlauben Sie Benutzern, sich bei Ihrem Repository anzumelden - cache_api: Aktiviert den Cache für API-Endpunkte. Experimental - contact_email: E-Mail-Adresse, die Kontakt-E-Mails erhalten soll - contact_email_to: E-Mail-Empfänger von Nachrichten, die über das Kontaktformular gesendet werden - doi_reader: Zeigen Sie die Fähigkeit, aus Datacite zu lesen, um Datensätze zu füllen. WIP nicht verwenden - doi_writer: Schreiben Sie DOIs für Datensätze. WIP nicht verwenden - email_format: Legen Sie eine Liste von E-Mail-Domains fest, die sich bei diesem Repository anmelden dürfen, z. B. (@ubiquitypress.com @gmail.com). Lassen Sie zwischen jeder Domäne ein einzelnes Leerzeichen. - email_subjet_prefix: Zeichenfolge, die den System-E-Mail-Betreffs vorangestellt werden soll. + allow_signup: Benutzern erlauben, sich für Ihr Repository anzumelden + cache_api: Cache für API-Endpunkte aktivieren. Experimentell + contact_email: Die E-Mail-Adresse, von der Systembenachrichtigungen gesendet werden. Eine zusätzliche Konfiguration ist erforderlich, um eine Adresse von anderen Domänen als der Domäne der Website hinzuzufügen + contact_email_to: Die E-Mail-Adresse, an die über die Kontaktseite gesendete Nachrichten gesendet werden + doi_reader: Zeigen Sie die Fähigkeit an, von Datacite zu lesen, um Datensätze zu füllen. WIP nicht verwenden + doi_writer: DOIs für Datensätze schreiben. WIP nicht verwenden + email_format: Legen Sie eine Liste von E-Mail-Domänen fest, die sich für dieses Repository anmelden dürfen, z. B. (@ubiquitypress.com @gmail.com). Lassen Sie jeweils ein Leerzeichen zwischen den Domänen. + email_subjet_prefix: Zeichenfolge, die vor System-E-Mail-Betreffs eingefügt wird. enable_oai_metadata: OAI-Link aktivieren oder deaktivieren - fcrepo_endpoint: - base_path: Fedora-Basispfad sollte mit einem Schrägstrich beginnen und nicht mit einem Schrägstrich enden - url: Fedora-URL sollte nicht mit einem Schrägstrich enden - file_acl: Deaktivieren Sie diese Option, wenn Sie ein Dateisystem wie Samba oder NFS verwenden, das das Festlegen von Zugriffssteuerungslisten nicht unterstützt - file_size_limit: Dieser sollte mindestens auf 536870912000 eingestellt sein - geonames_username: Registrieren Sie sich unter http://www.geonames.org/manageaccount - gtm_id: Die ID Ihres Google Tag Manager-Kontos - is_public: 'Können Benutzer Ihre Seite auf der Startseite entdecken oder auf Ihre Seiten zugreifen, ohne einen speziellen Benutzernamen / Passwort?' fcrepo_endpoint: base_path: Der Fedora-Basispfad sollte mit einem Schrägstrich beginnen UND nicht mit einem Schrägstrich enden url: Die Fedora-URL sollte nicht mit einem Schrägstrich enden - name: Ein einzelner oder mit Bindestrichen versehener Name, der für technische Aspekte des Repositories verwendet wird (z.B. "acme" oder "acme-bibliothek"). - gtm_id: Die ID Ihres Google Tag Manager-Kontos + file_acl: Deaktivieren Sie dies, wenn Sie ein Dateisystem wie Samba oder NFS verwenden, das das Festlegen von Zugriffssteuerungslisten nicht unterstützt + file_size_limit: Dies sollte mindestens auf 536870912000 eingestellt sein + geonames_username: Registrieren Sie sich unter http://www.geonames.org/manageaccount google_analytics_id: Die ID Ihres Google Analytics-Kontos - google_oauth_app_name: Der Name der Google-Anwendung in der Google API-Konsole - google_oauth_app_version: Die Version der Google-Anwendung in der Google API-Konsole - google_oauth_private_key_value: Der Wert der p12-Datei mit Base64-Verschlüsselung. (Sie können den privaten Schlüsselwert ODER den Pfad verwenden; der Wert hat Vorrang) - google_oauth_private_key_path: Der vollständige Pfad zu Ihrer p12-Schlüsseldatei. (Sie können den privaten Schlüsselwert ODER den Pfad verwenden; der Wert hat Vorrang) - google_oauth_private_key_secret: Das Geheimnis, das Sie beim Erstellen des p12-Schlüssels angegeben haben - google_oauth_client_email: OAuth-Client-E-Mail-Adresse - contact_email: Die E-Mail-Adresse, an die über die Kontaktseite gesendete Nachrichten gesendet werden - weekly_email_list: Liste von E-Mail-Adressen, an die der wöchentliche Bericht gesendet wird. Lassen Sie jeweils ein Leerzeichen zwischen den E-Mails + gtm_id: Die ID Ihres Google Tag Manager-Kontos + is_public: Können Benutzer Ihre Seite auf der Startseite entdecken oder auf Ihre Seiten zugreifen, ohne einen speziellen Benutzernamen / Passwort? + locale_name: 'Der Name des mandantenspezifischen Gebietsschemasuffix, der ihren locale.yml-Dateien hinzugefügt wird. Es sollten nur alphabetische Zeichen hinzugefügt werden, keine Symbole oder Zahlen, diese werden dann großgeschrieben. + + ' monthly_email_list: Liste von E-Mail-Adressen, an die der monatliche Bericht gesendet wird. Lassen Sie jeweils ein Leerzeichen zwischen den E-Mails - yearly_email_list: Liste von E-Mail-Adressen, an die der jährliche Bericht gesendet wird. Lassen Sie jeweils ein Leerzeichen zwischen den E-Mails - email_format: Legen Sie eine Liste von E-Mail-Domänen fest, die sich für dieses Repository anmelden dürfen, z. B. (@ubiquitypress.com @gmail.com). Lassen Sie jeweils ein Leerzeichen zwischen den Domänen. - allow_signup: Benutzern erlauben, sich für Ihr Repository anzumelden - enable_oai_metadata: OAI-Link aktivieren oder deaktivieren - shared_login: Gemeinsamen Login aktivieren oder deaktivieren - file_size_limit: Dies sollte mindestens auf 536870912000 eingestellt sein - locale_name: | - Der Name des mandantenspezifischen Gebietsschemasuffix, der ihren locale.yml-Dateien hinzugefügt wird. Es sollten nur alphabetische Zeichen hinzugefügt werden, keine Symbole oder Zahlen, diese werden dann großgeschrieben. + name: Ein einzelner oder mit Bindestrichen versehener Name, der für technische Aspekte des Repositories verwendet wird (z.B. "acme" oder "acme-bibliothek"). oai_admin_email: OAI-Endpunkt-Kontakt-E-Mail-Adresse - doi_reader: Zeigen Sie die Fähigkeit an, von Datacite zu lesen, um Datensätze zu füllen. WIP nicht verwenden - doi_writer: DOIs für Datensätze schreiben. WIP nicht verwenden - cache_api: Cache für API-Endpunkte aktivieren. Experimentell - email_subjet_prefix: Zeichenfolge, die vor System-E-Mail-Betreffs eingefügt wird. - contact_email_to: Die E-Mail-Adresse, von der Systembenachrichtigungen gesendet werden. Eine zusätzliche Konfiguration ist erforderlich, um eine Adresse von anderen Domänen als der Domäne der Website hinzuzufügen - geonames_username: Registrieren Sie sich unter http://www.geonames.org/manageaccount - file_acl: Deaktivieren Sie dies, wenn Sie ein Dateisystem wie Samba oder NFS verwenden, das das Festlegen von Zugriffssteuerungslisten nicht unterstützt + shared_login: Gemeinsamen Login aktivieren oder deaktivieren ssl_configured: Setzen Sie es auf wahr, wenn Sie https verwenden + weekly_email_list: Liste von E-Mail-Adressen, an die der wöchentliche Bericht gesendet wird. Lassen Sie jeweils ein Leerzeichen zwischen den E-Mails + yearly_email_list: Liste von E-Mail-Adressen, an die der jährliche Bericht gesendet wird. Lassen Sie jeweils ein Leerzeichen zwischen den E-Mails hyku_group: description: Eine kurze Zusammenfassung der Rolle der Gruppe user: @@ -87,11 +66,11 @@ de: email: E-Mail-Adresse user_search: uq: Benutzer suchen - 'no': 'Nein' + 'no': Nein placeholders: user_search: uq: Name oder Benutzername required: mark: "*" text: erforderlich - 'yes': 'Ja' + 'yes': Ja diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 9f7f082f4..f5612f62c 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -13,6 +13,8 @@ en: url: Fedora URL should not end with a slash name: A single or hyphenated name used for technical aspects of the repository (e.g., "acme" or "acme-library"). gtm_id: The ID of your Google Tag Manager account + google_analytics_id: The ID of your Google Analytics account + contact_email_to: The email address that messages submitted via the contact page are sent to weekly_email_list: List of email addresses to email the weekly report. Leave a single space between each email monthly_email_list: List of email addresses to email the monthly report. Leave a single space between each email yearly_email_list: List of email addresses to email the yearly report. Leave a single space between each email @@ -29,7 +31,6 @@ en: cache_api: Turns on cache for API endpoints. Experimental email_subjet_prefix: String to put in front of system email subjects. contact_email: The email address that system notifications will be sent from. Additional configuration is required to add an address from domains other than the site's domain - contact_email_to: The email address that messages submitted via the contact page are sent to geonames_username: Register at http://www.geonames.org/manageaccount file_acl: Turn off if using a file system like samba or nfs that does not support setting access control lists ssl_configured: Set it true if using https diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml index 15e7d41b3..a1b2d1268 100644 --- a/config/locales/simple_form.es.yml +++ b/config/locales/simple_form.es.yml @@ -6,37 +6,35 @@ es: default_message: 'Por favor, revise los problemas a continuación:' hints: account: - admin_emails: Introduce una dirección de correo electrónico a la vez - allow_signup: Permita que los usuarios se registren en su repositorio - cache_api: Activa la memoria caché para los puntos finales de la API. Experimental - contact_email: Dirección de correo electrónico que debe recibir correos electrónicos de contacto - contact_email_to: Destinatario de correo electrónico de los mensajes enviados a través del formulario de contacto - doi_reader: Mostrar la capacidad de leer de Datacite para completar registros. WIP no usar - doi_writer: Escriba DOI para los registros. WIP no usar - email_format: Establezca una lista de dominios de correo electrónico que pueden registrarse en este repositorio, por ejemplo, (@ubiquitypress.com @gmail.com). Deje un solo espacio entre cada dominio. - email_subjet_prefix: Cadena para colocar delante de los asuntos de correo electrónico del sistema. + admin_emails: Introduzca una dirección de correo electrónico a la vez + allow_signup: Permitir a los usuarios registrarse en su repositorio + cache_api: Activa la caché para puntos finales de API. Experimental + contact_email: La dirección de correo electrónico desde la que se enviarán las notificaciones del sistema. Se requiere una configuración adicional para agregar una dirección de dominios distintos al dominio del sitio + contact_email_to: La dirección de correo electrónico a la que se envían los mensajes enviados a través de la página de contacto + doi_reader: Mostrar la capacidad de leer de Datacite para rellenar registros. WIP no usar + doi_writer: Escribir DOIs para registros. WIP no usar + email_format: Establezca una lista de dominios de correo electrónico que pueden registrarse en este repositorio, por ejemplo (@ubiquitypress.com @gmail.com). Deje un espacio entre cada dominio. + email_subjet_prefix: Cadena para poner al frente de los asuntos de correo electrónico del sistema. enable_oai_metadata: Habilitar o deshabilitar el enlace OAI fcrepo_endpoint: base_path: La ruta base de Fedora debe comenzar con una barra y NO terminar con una barra url: La URL de Fedora no debe terminar con una barra - file_acl: Desactívelo si usa un sistema de archivos como samba o nfs que no admita la configuración de listas de control de acceso - file_size_limit: Esto debe establecerse en al menos 536870912000 + file_acl: Desactívelo si usa un sistema de archivos como samba o nfs que no admite listas de control de acceso + file_size_limit: Esto debe establecerse al menos en 536870912000 geonames_username: Regístrese en http://www.geonames.org/manageaccount - gtm_id: El ID de su cuenta de Google Tag Manager - is_public: "¿Los usuarios pueden descubrir su sitio en la página de inicio o acceder a sus páginas sin un nombre de usuario/contraseña especiales?" - locale_name: 'El nombre del sufijo de configuración regional específico del arrendatario agregado a sus archivos locale.yml. Solo se deben agregar caracteres alfabéticos, sin símbolos ni números, estos se escribirán en mayúscula.' - name: Un nombre único o con guión utilizado para los aspectos técnicos del repositorio (por ejemplo, "acme" o "acme-library"). - oai_admin_email: Dirección de correo electrónico de contacto del terminal OAI - shared_login: Habilitar o deshabilitar el inicio de sesión compartido - ssl_configured: Establézcalo en verdadero si usa https google_analytics_id: La ID de su cuenta de Google Analytics - google_oauth_app_name: El nombre de la aplicación de Google en la consola de API de Google - google_oauth_app_version: La versión de la aplicación de Google en la consola de API de Google - google_oauth_private_key_value: El valor del archivo p12 con cifrado base64. (Puede usar el valor de la clave privada O el camino; el valor tiene prioridad) - google_oauth_private_key_path: La ruta completa a su archivo p12, archivo clave. (Puede usar el valor de la clave privada O el camino; el valor tiene prioridad) - google_oauth_private_key_secret: El secreto proporcionado cuando creó la clave p12 - google_oauth_client_email: Dirección de correo electrónico del cliente OAuth + gtm_id: La ID de su cuenta de Google Tag Manager + is_public: "¿Pueden los usuarios descubrir su sitio en la página principal o acceder a sus páginas sin un nombre de usuario/contraseña especial?" + locale_name: 'El nombre del sufijo de configuración regional específico del inquilino añadido a sus archivos locale.yml. Solo se deben agregar caracteres alfabéticos, no símbolos o números, estos se capitalizarán. + + ' + monthly_email_list: Lista de direcciones de correo electrónico para enviar el informe mensual. Deje un espacio entre cada correo electrónico + name: Un nombre único o con guiones utilizado para aspectos técnicos del repositorio (por ejemplo, "acme" o "acme-biblioteca"). + oai_admin_email: Dirección de correo electrónico de contacto del punto final OAI shared_login: Habilitar o deshabilitar inicio de sesión compartido + ssl_configured: Establézcalo como verdadero si está utilizando https + weekly_email_list: Lista de direcciones de correo electrónico para enviar el informe semanal. Deje un espacio entre cada correo electrónico + yearly_email_list: Lista de direcciones de correo electrónico para enviar el informe anual. Deje un espacio entre cada correo electrónico hyku_group: description: Un breve resumen del papel del grupo user: @@ -75,4 +73,4 @@ es: required: mark: "*" text: requerido - 'yes': 'Sí' + 'yes': Sí diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index a938ff739..d7c08b62d 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -7,49 +7,34 @@ fr: hints: account: admin_emails: Entrez une adresse e-mail à la fois - allow_signup: Autoriser les utilisateurs à s'inscrire à votre référentiel - cache_api: Active le cache pour les points de terminaison d'API. Expérimental - contact_email: Adresse e-mail qui doit recevoir les e-mails de contact - contact_email_to: Email destinataire des messages envoyés via le formulaire de contact - doi_reader: Afficher la possibilité de lire à partir de Datacite pour remplir les enregistrements. WIP ne pas utiliser - doi_writer: Rédigez des DOI pour les enregistrements. WIP ne pas utiliser - email_format: Définissez une liste de domaines de messagerie autorisés à s'inscrire à ce référentiel, par exemple (@ubiquitypress.com @gmail.com). Laissez un seul espace entre chaque domaine. - email_subjet_prefix: Chaîne à placer devant les sujets des e-mails système. + allow_signup: Autoriser les utilisateurs à s'inscrire à votre dépôt + cache_api: Active le cache pour les points d'accès API. Expérimental + contact_email: L'adresse e-mail à partir de laquelle les notifications système seront envoyées. Une configuration supplémentaire est nécessaire pour ajouter une adresse provenant de domaines autres que le domaine du site + contact_email_to: L'adresse e-mail à laquelle les messages soumis via la page de contact sont envoyés + doi_reader: Afficher la capacité à lire depuis Datacite pour remplir les enregistrements. WIP ne pas utiliser + doi_writer: Écrire des DOIs pour les enregistrements. WIP ne pas utiliser + email_format: Définissez une liste de domaines de messagerie autorisés à s'inscrire à ce dépôt, par exemple (@ubiquitypress.com @gmail.com). Laissez un espace entre chaque domaine. + email_subjet_prefix: Chaîne à mettre devant les sujets des e-mails système. enable_oai_metadata: Activer ou désactiver le lien OAI fcrepo_endpoint: - base_path: Le chemin de base de Fedora doit commencer par une barre oblique ET ne pas se terminer par une barre oblique - url: Fedora URL ne devrait pas se terminer par une barre oblique - file_acl: Désactiver si vous utilisez un système de fichiers comme samba ou nfs qui ne prend pas en charge la définition de listes de contrôle d'accès - file_size_limit: Il doit être défini sur au moins 536870912000 + base_path: Le chemin de base de Fedora doit commencer par un slash et ne PAS se terminer par un slash + url: L'URL de Fedora ne doit pas se terminer par un slash + file_acl: Désactivez si vous utilisez un système de fichiers comme samba ou nfs qui ne prend pas en charge les listes de contrôle d'accès + file_size_limit: Ceci doit être réglé à au moins 536870912000 geonames_username: Inscrivez-vous sur http://www.geonames.org/manageaccount google_analytics_id: L'ID de votre compte Google Analytics - google_oauth_app_name: Le nom de l'application Google dans la console API de Google - google_oauth_app_version: La version de l'application Google dans la console API de Google - google_oauth_private_key_value: La valeur du fichier p12 avec cryptage base64. (Vous pouvez utiliser la valeur de la clé privée OU le chemin ; la valeur prime) - google_oauth_private_key_path: Le chemin complet vers votre fichier p12, fichier clé. (Vous pouvez utiliser la valeur de la clé privée OU le chemin ; la valeur prime) - google_oauth_private_key_secret: Le secret fourni lorsque vous avez créé la clé p12 - google_oauth_client_email: Adresse e-mail du client OAuth - gtm_id: L'identifiant de votre compte Google Tag Manager - is_public: Les utilisateurs peuvent-ils découvrir votre site sur la page d'accueil ou accéder à vos pages sans nom d'utilisateur/mot de passe particulier ? - locale_name: 'Le nom du suffixe de paramètres régionaux spécifiques au locataire ajouté à leurs fichiers locale.yml. Seuls les caractères alphabétiques doivent être ajoutés, pas de symboles ni de chiffres, ceux-ci seront alors en majuscules. + gtm_id: L'ID de votre compte Google Tag Manager + is_public: Les utilisateurs peuvent-ils découvrir votre site sur la page d'accueil ou accéder à vos pages sans identifiant/mot de passe spécifique? + locale_name: 'Le nom du suffixe de locale spécifique au locataire ajouté à leurs fichiers locale.yml. Seuls les caractères alphabétiques doivent être ajoutés, pas de symboles ou de chiffres, ceux-ci seront alors mis en majuscule. ' - monthly_email_list: Liste des adresses e-mail pour envoyer le rapport mensuel. Laissez un seul espace entre chaque email - name: Nom unique ou un trait d'union utilisé pour les aspects techniques du référentiel (par exemple, "acme" ou "acme-bibliothèque"). - oai_admin_email: Adresse e-mail de contact du point de terminaison OAI - shared_login: Activer ou désactiver la connexion partagée - file_size_limit: Ceci doit être réglé à au moins 536870912000 - locale_name: | - Le nom du suffixe de locale spécifique au locataire ajouté à leurs fichiers locale.yml. Seuls les caractères alphabétiques doivent être ajoutés, pas de symboles ou de chiffres, ceux-ci seront alors mis en majuscule. + monthly_email_list: Liste des adresses e-mail pour envoyer le rapport mensuel. Laissez un espace entre chaque e-mail + name: Un nom simple ou avec des tirets utilisé pour les aspects techniques du dépôt (par exemple, "acme" ou "acme-bibliothèque"). oai_admin_email: Adresse e-mail de contact du point d'accès OAI - doi_reader: Afficher la capacité à lire depuis Datacite pour remplir les enregistrements. WIP ne pas utiliser - doi_writer: Écrire des DOIs pour les enregistrements. WIP ne pas utiliser - cache_api: Active le cache pour les points d'accès API. Expérimental - email_subjet_prefix: Chaîne à mettre devant les sujets des e-mails système. - contact_email_to: L'adresse e-mail à partir de laquelle les notifications système seront envoyées. Une configuration supplémentaire est nécessaire pour ajouter une adresse provenant de domaines autres que le domaine du site - geonames_username: Inscrivez-vous sur http://www.geonames.org/manageaccount - file_acl: Désactivez si vous utilisez un système de fichiers comme samba ou nfs qui ne prend pas en charge les listes de contrôle d'accès + shared_login: Activer ou désactiver la connexion partagée ssl_configured: Mettez-le sur vrai si vous utilisez https + weekly_email_list: Liste des adresses e-mail pour envoyer le rapport hebdomadaire. Laissez un espace entre chaque e-mail + yearly_email_list: Liste des adresses e-mail pour envoyer le rapport annuel. Laissez un espace entre chaque e-mail hyku_group: description: Un bref résumé du rôle du groupe user: @@ -81,11 +66,11 @@ fr: email: Adresse e-mail user_search: uq: Recherche d'utilisateur - 'no': 'Non' + 'no': Non placeholders: user_search: uq: Nom ou nom d'utilisateur required: mark: "*" text: requis - 'yes': 'Oui' + 'yes': Oui diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index 3c80f0470..19e5647ee 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -1,5 +1,5 @@ --- -eit: +it: simple_form: cancel: Annulla error_notification: @@ -7,49 +7,34 @@ eit: hints: account: admin_emails: Inserisci un indirizzo email alla volta - allow_signup: Consenti agli utenti di registrarsi al tuo repository - cache_api: Attiva la cache per gli endpoint API. Sperimentale - contact_email: Indirizzo e-mail che dovrebbe ricevere le e-mail di contatto - contact_email_to: Email destinatario dei messaggi inviati tramite il modulo di contatto - doi_reader: Mostra la capacità di leggere da Datacite per popolare i record. WIP non utilizzare - doi_writer: Scrivi DOI per i record. WIP non utilizzare - email_format: Impostare un elenco di domini di posta elettronica a cui è consentito registrarsi a questo repository, ad esempio (@ubiquitypress.com @gmail.com). Lascia un singolo spazio tra ogni dominio. + allow_signup: Permetti agli utenti di iscriversi al tuo repository + cache_api: Abilita la cache per gli endpoint API. Sperimentale + contact_email: L'indirizzo email da cui saranno inviate le notifiche di sistema. È richiesta una configurazione aggiuntiva per aggiungere un indirizzo da domini diversi dal dominio del sito + contact_email_to: L'indirizzo email al quale i messaggi inviati tramite la pagina di contatto sono inviati + doi_reader: Mostra la capacità di leggere da Datacite per popolare i record. Lavoro in corso, non utilizzare + doi_writer: Scrivi DOI per i record. Lavoro in corso, non utilizzare + email_format: Imposta una lista di domini email che possono iscriversi a questo repository es. (@ubiquitypress.com @gmail.com). Lascia uno spazio tra ogni dominio. email_subjet_prefix: Stringa da mettere davanti agli oggetti delle email di sistema. - enable_oai_metadata: Abilita o disabilita il collegamento OAI - + enable_oai_metadata: Abilita o disabilita il link OAI fcrepo_endpoint: base_path: Il percorso base di Fedora dovrebbe iniziare con una barra e NON terminare con una barra url: L'URL di Fedora non dovrebbe terminare con una barra - name: Un nome singolo o con trattino usato per gli aspetti tecnici del repository (ad es., "acme" o "acme-biblioteca"). - gtm_id: L'ID del tuo account Google Tag Manager + file_acl: Disattiva se stai utilizzando un sistema di file come samba o nfs che non supporta le liste di controllo degli accessi + file_size_limit: Questo dovrebbe essere impostato almeno a 536870912000 + geonames_username: Registrati su http://www.geonames.org/manageaccount google_analytics_id: L'ID del tuo account Google Analytics - google_oauth_app_name: Il nome dell'applicazione Google nella console API di Google - google_oauth_app_version: La versione dell'applicazione Google nella console API di Google - google_oauth_private_key_value: Il valore del file p12 con crittografia base64. (Puoi utilizzare il valore della chiave privata OPPURE il percorso; il valore ha la precedenza) - google_oauth_private_key_path: Il percorso completo del tuo file p12, file chiave. (Puoi utilizzare il valore della chiave privata OPPURE il percorso; il valore ha la precedenza) - google_oauth_private_key_secret: Il segreto fornito quando hai creato la chiave p12 - google_oauth_client_email: Indirizzo email del client OAuth - contact_email: L'indirizzo email al quale i messaggi inviati tramite la pagina di contatto sono inviati - weekly_email_list: Lista di indirizzi email ai quali inviare il rapporto settimanale. Lascia uno spazio tra ogni email + gtm_id: L'ID del tuo account Google Tag Manager + is_public: Gli utenti possono scoprire il tuo sito nella pagina principale o accedere alle tue pagine senza un nome utente/password speciale? + locale_name: 'Il nome del suffisso specifico del tenant aggiunto ai loro file locale.yml. Aggiungi solo caratteri alfabetici, niente simboli o numeri, questi saranno poi capitalizzati. + + ' monthly_email_list: Lista di indirizzi email ai quali inviare il rapporto mensile. Lascia uno spazio tra ogni email - yearly_email_list: Lista di indirizzi email ai quali inviare il rapporto annuale. Lascia uno spazio tra ogni email - email_format: Imposta una lista di domini email che possono iscriversi a questo repository es. (@ubiquitypress.com @gmail.com). Lascia uno spazio tra ogni dominio. - allow_signup: Permetti agli utenti di iscriversi al tuo repository - enable_oai_metadata: Abilita o disabilita il link OAI - shared_login: Abilita o disabilita l'accesso condiviso - file_size_limit: Questo dovrebbe essere impostato almeno a 536870912000 - is_public: 'Gli utenti possono scoprire il tuo sito nella pagina principale o accedere alle tue pagine senza un nome utente/password speciale?' - locale_name: | - Il nome del suffisso specifico del tenant aggiunto ai loro file locale.yml. Aggiungi solo caratteri alfabetici, niente simboli o numeri, questi saranno poi capitalizzati. + name: Un nome singolo o con trattino usato per gli aspetti tecnici del repository (ad es., "acme" o "acme-biblioteca"). oai_admin_email: Indirizzo email di contatto dell'endpoint OAI - doi_reader: Mostra la capacità di leggere da Datacite per popolare i record. Lavoro in corso, non utilizzare - doi_writer: Scrivi DOI per i record. Lavoro in corso, non utilizzare - cache_api: Abilita la cache per gli endpoint API. Sperimentale - email_subjet_prefix: Stringa da mettere davanti agli oggetti delle email di sistema. - contact_email_to: L'indirizzo email da cui saranno inviate le notifiche di sistema. È richiesta una configurazione aggiuntiva per aggiungere un indirizzo da domini diversi dal dominio del sito - geonames_username: Registrati su http://www.geonames.org/manageaccount - file_acl: Disattiva se stai utilizzando un sistema di file come samba o nfs che non supporta le liste di controllo degli accessi + shared_login: Abilita o disabilita l'accesso condiviso ssl_configured: Impostalo su vero se stai utilizzando https + weekly_email_list: Lista di indirizzi email ai quali inviare il rapporto settimanale. Lascia uno spazio tra ogni email + yearly_email_list: Lista di indirizzi email ai quali inviare il rapporto annuale. Lascia uno spazio tra ogni email hyku_group: description: Un breve riassunto del ruolo del gruppo user: @@ -88,4 +73,4 @@ eit: required: mark: "*" text: richiesto - 'yes': 'Sì' + 'yes': Sì diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 366ad7478..b9d839343 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -6,50 +6,35 @@ pt-BR: default_message: 'Por favor, reveja os problemas abaixo:' hints: account: - admin_emails: Digite um endereço de email de cada vez - allow_signup: Permitir que os usuários se inscrevam em seu repositório - cache_api: Ativa o cache para terminais de API. Experimental - contact_email: Endereço de e-mail que deve receber e-mails de contato - contact_email_to: Destinatário de e-mail de mensagens enviadas por meio do formulário de contato - doi_reader: Mostrar capacidade de ler do Datacite para preencher registros. WIP não use - doi_writer: Escreva DOIs para registros. WIP não use - email_format: Defina uma lista de domínios de e-mail que podem se inscrever neste repositório, por exemplo (@ubiquitypress.com @gmail.com). Deixe um único espaço entre cada domínio. - email_subjet_prefix: String a ser colocada antes dos assuntos do e-mail do sistema. - enable_oai_metadata: Ativar ou desativar o link OAI + admin_emails: Insira um endereço de e-mail por vez + allow_signup: Permitir que usuários se inscrevam no seu repositório + cache_api: Ativar cache para pontos finais de API. Experimental + contact_email: O endereço de e-mail do qual as notificações do sistema serão enviadas. Uma configuração adicional é necessária para adicionar um endereço de domínios diferentes do domínio do site + contact_email_to: O endereço de e-mail para o qual as mensagens enviadas via página de contato são enviadas + doi_reader: Mostrar capacidade de ler da Datacite para preencher registros. Em desenvolvimento, não use + doi_writer: Escrever DOIs para registros. Em desenvolvimento, não use + email_format: Defina uma lista de domínios de e-mail que são permitidos para se inscrever neste repositório, por exemplo (@ubiquitypress.com @gmail.com). Deixe um espaço único entre cada domínio. + email_subjet_prefix: String para colocar na frente dos assuntos dos e-mails do sistema. + enable_oai_metadata: Habilitar ou desabilitar link OAI fcrepo_endpoint: - base_path: O caminho base do Fedora deve começar com uma barra E não terminar com uma barra - url: O URL do Fedora não deve terminar com uma barra - file_acl: Desligue se estiver usando um sistema de arquivos como samba ou nfs que não suporta a configuração de listas de controle de acesso - file_size_limit: Isso deve ser definido para pelo menos 536870912000 + base_path: O caminho base do Fedora deve começar com uma barra e NÃO terminar com uma barra + url: A URL do Fedora não deve terminar com uma barra + file_acl: Desative se estiver usando um sistema de arquivos como samba ou nfs que não suporta listas de controle de acesso + file_size_limit: Isso deve ser configurado para pelo menos 536870912000 geonames_username: Registre-se em http://www.geonames.org/manageaccount google_analytics_id: O ID da sua conta no Google Analytics - google_oauth_app_name: O nome do aplicativo Google no console da API do Google - google_oauth_app_version: A versão do aplicativo Google no console da API do Google - google_oauth_private_key_value: O valor do arquivo p12 com criptografia base64. (Você pode usar o valor da chave privada OU o caminho; o valor tem precedência) - google_oauth_private_key_path: O caminho completo para o seu arquivo p12, arquivo de chave. (Você pode usar o valor da chave privada OU o caminho; o valor tem precedência) - google_oauth_private_key_secret: O segredo fornecido quando você criou a chave p12 - google_oauth_client_email: Endereço de e-mail do cliente OAuth - gtm_id: O ID da sua conta do Gerenciador de tags do Google + gtm_id: O ID da sua conta no Google Tag Manager is_public: Os usuários podem descobrir seu site na página inicial ou acessar suas páginas sem um nome de usuário/senha especial? - locale_name: 'O nome do sufixo de localidade específico do locatário incluído em seus arquivos locale.yml. Apenas caracteres alfabéticos devem ser adicionados, sem símbolos ou números, estes serão então maiúsculos. + locale_name: 'O nome do sufixo específico do inquilino adicionado aos seus arquivos locale.yml. Apenas caracteres alfabéticos devem ser adicionados, sem símbolos ou números, estes serão então capitalizados. ' - monthly_email_list: Lista de endereços de e-mail para enviar o relatório mensal por e-mail. Deixe um único espaço entre cada e-mail - name: Um nome único ou hifenizado usado para aspectos técnicos do repositório (por exemplo, "acme" ou "acme-library"). - oai_admin_email: Endereço de e-mail de contato do terminal OAI - shared_login: Habilitar ou desabilitar login compartilhado - file_size_limit: Isso deve ser configurado para pelo menos 536870912000 - locale_name: | - O nome do sufixo específico do inquilino adicionado aos seus arquivos locale.yml. Apenas caracteres alfabéticos devem ser adicionados, sem símbolos ou números, estes serão então capitalizados. + monthly_email_list: Lista de endereços de e-mail para enviar o relatório mensal. Deixe um espaço único entre cada e-mail + name: Um nome simples ou hifenizado usado para aspectos técnicos do repositório (por exemplo, "acme" ou "acme-biblioteca"). oai_admin_email: Endereço de e-mail de contato do ponto final OAI - doi_reader: Mostrar capacidade de ler da Datacite para preencher registros. Em desenvolvimento, não use - doi_writer: Escrever DOIs para registros. Em desenvolvimento, não use - cache_api: Ativar cache para pontos finais de API. Experimental - email_subjet_prefix: String para colocar na frente dos assuntos dos e-mails do sistema. - contact_email_to: O endereço de e-mail do qual as notificações do sistema serão enviadas. Uma configuração adicional é necessária para adicionar um endereço de domínios diferentes do domínio do site - geonames_username: Registre-se em http://www.geonames.org/manageaccount - file_acl: Desative se estiver usando um sistema de arquivos como samba ou nfs que não suporta listas de controle de acesso + shared_login: Habilitar ou desabilitar login compartilhado ssl_configured: Defina como verdadeiro se estiver usando https + weekly_email_list: Lista de endereços de e-mail para enviar o relatório semanal. Deixe um espaço único entre cada e-mail + yearly_email_list: Lista de endereços de e-mail para enviar o relatório anual. Deixe um espaço único entre cada e-mail hyku_group: description: Um breve resumo do papel do grupo user: @@ -81,11 +66,11 @@ pt-BR: email: Endereço de e-mail user_search: uq: Pesquisar por usuário - 'no': 'Não' + 'no': Não placeholders: user_search: uq: Nome ou nome de usuário required: mark: "*" text: obrigatório - 'yes': 'Sim' + 'yes': Sim diff --git a/config/locales/simple_form.zh.yml b/config/locales/simple_form.zh.yml index 9b8745ed4..f83f657a7 100644 --- a/config/locales/simple_form.zh.yml +++ b/config/locales/simple_form.zh.yml @@ -3,53 +3,38 @@ zh: simple_form: cancel: 取消 error_notification: - default_message: '请检查以下问题:' + default_message: 请检查以下问题: hints: account: admin_emails: 一次输入一个电子邮件地址 - allow_signup: 允许用户注册到您的存储库 - cache_api: 为 API 端点打开缓存。实验性的 - contact_email: 应接收联系电子邮件的电子邮件地址 - contact_email_to: 通过联系表发送的邮件的电子邮件收件人 - doi_reader: 显示从 Datacite 读取数据以填充记录的能力。 WIP 不使用 - doi_writer: 为记录写 DOI。 WIP 不使用 - email_format: 设置允许注册此存储库的电子邮件域列表,例如 (@ubiquitypress.com @gmail.com)。在每个域之间留一个空格。 + allow_signup: 允许用户注册您的存储库 + cache_api: 打开API端点的缓存。实验性 + contact_email: 系统通知将从其中发送的电子邮件地址。需要额外的配置才能从网站域以外的域添加地址 + contact_email_to: 通过联系页面提交的消息发送到的电子邮件地址 + doi_reader: 显示从Datacite读取以填充记录的能力。仍在进行中,请勿使用 + doi_writer: 为记录编写DOIs。仍在进行中,请勿使用 + email_format: 设置允许注册此存储库的电子邮件域的列表,例如(@ubiquitypress.com @gmail.com)。每个域之间留一个空格。 email_subjet_prefix: 放在系统电子邮件主题前面的字符串。 - enable_oai_metadata: 启用或禁用 OAI 链接 + enable_oai_metadata: 启用或禁用OAI链接 fcrepo_endpoint: - base_path: Fedora 基本路径应以斜线开始,而不是以斜杠结尾 - url: Fedora 网址不应以斜线结尾 - file_acl: 如果使用不支持设置访问控制列表的文件系统(如 samba 或 nfs),请关闭 - file_size_limit: 这应该至少设置为 536870912000 - geonames_username: 在 http://www.geonames.org/manageaccount 注册 + base_path: Fedora基本路径应该以斜杠开始且不应该以斜杠结束 + url: Fedora URL不应该以斜杠结束 + file_acl: 如果使用不支持设置访问控制列表的文件系统(如samba或nfs),请关闭它 + file_size_limit: 此值应至少设置为536870912000 + geonames_username: 在http://www.geonames.org/manageaccount上注册 google_analytics_id: 您的Google Analytics帐户ID - google_oauth_app_name: Google API控制台中的Google应用程序名称 - google_oauth_app_version: Google API控制台中的Google应用版本 - google_oauth_private_key_value: 带有base64加密的p12文件的值。(您可以使用私钥值或路径;值优先) - google_oauth_private_key_path: 您的p12密钥文件的完整路径。(您可以使用私钥值或路径;值优先) - google_oauth_private_key_secret: 创建p12密钥时提供的秘密 - google_oauth_client_email: OAuth客户电子邮件地址 - gtm_id: 您的 Google 跟踪代码管理器帐户的 ID - is_public: 用户是否可以在主页上发现您的站点或在没有特殊用户名/密码的情况下访问您的页面? - locale_name: '添加到其 locale.yml 文件的租户特定区域设置后缀的名称。只能添加字母字符,不能添加符号或数字,这些将被大写。 + gtm_id: 您的Google标签管理器帐户ID + is_public: 用户可以在主页上发现您的网站,或者不需要特殊的用户名/密码就可以访问您的页面吗? + locale_name: '添加到其locale.yml文件的租户特定语言环境后缀的名称。只应添加字母字符,不应添加符号或数字,这些将随后大写。 ' - monthly_email_list: 用于发送月度报告的电子邮件地址列表。在每封电子邮件之间留一个空格 - name: 用于存储库技术方面的单个或连字符名称(例如“acme”或“acme-library”)。 - oai_admin_email: OAI 端点联系人电子邮件地址 - shared_login: 启用或禁用共享登录 - file_size_limit: 此值应至少设置为536870912000 - locale_name: | - 添加到其locale.yml文件的租户特定语言环境后缀的名称。只应添加字母字符,不应添加符号或数字,这些将随后大写。 + monthly_email_list: 每月报告的电子邮件地址列表。每个电子邮件之间留一个空格 + name: 用于存储技术方面的单一或连字符名称(例如,“acme”或“acme-library”)。 oai_admin_email: OAI端点联系电子邮件地址 - doi_reader: 显示从Datacite读取以填充记录的能力。仍在进行中,请勿使用 - doi_writer: 为记录编写DOIs。仍在进行中,请勿使用 - cache_api: 打开API端点的缓存。实验性 - email_subjet_prefix: 放在系统电子邮件主题前面的字符串。 - contact_email_to: 系统通知将从其中发送的电子邮件地址。需要额外的配置才能从网站域以外的域添加地址 - geonames_username: 在http://www.geonames.org/manageaccount上注册 - file_acl: 如果使用不支持设置访问控制列表的文件系统(如samba或nfs),请关闭它 + shared_login: 启用或禁用共享登录 ssl_configured: 如果使用https,请将其设置为true + weekly_email_list: 每周报告的电子邮件地址列表。每个电子邮件之间留一个空格 + yearly_email_list: 每年报告的电子邮件地址列表。每个电子邮件之间留一个空格 hyku_group: description: 该组的角色简介 user: @@ -81,11 +66,11 @@ zh: email: 电子邮件地址 user_search: uq: 用户搜索 - 'no': '否' + 'no': 否 placeholders: user_search: uq: 名称或用户名 required: mark: "*" text: 必填 - 'yes': '是' + 'yes': 是 diff --git a/config/locales/zh.yml b/config/locales/zh.yml index b37f0c56a..ccf4770fa 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -1,5 +1,12 @@ --- zh: + errors: + messages: + valid_embed_url: "必须是有效的YouTube或Vimeo嵌入URL。" + activefedora: + models: + generic_work: 工作 + image: 图像 activerecord: attributes: site: @@ -7,6 +14,8 @@ zh: application: tagline: 下一代存储库解决方案 helpers: + action: + become: 成為 submit: add_role_to_group: submit: 加 @@ -163,6 +172,8 @@ zh: forms: banner_image: hint: 要将图像用作标头广告背景,您应该使用至少120像素高和1200像素宽的图像(JPG,GIF或PNG)。为了获得最佳效果,请使用至少1800像素宽的图像。 + collection_banner_text_color: + hint: 集合横幅内文本(标题和上次更新日期)的颜色。 custom_css: confirm: 自定义CSS将始终覆盖其他主题选择。继续? warning: 如果您的主题自定义设置似乎无法正确应用,请确认自定义CSS不会覆盖它们。 @@ -172,7 +183,7 @@ zh: alert: 请至少上传一个文件,然后再提交。 hint: 对于默认图像,您应该使用高度和宽度尺寸(宽度为100像素,高度为100像素)的图像(JPG,GIF或PNG) directory_image: - hint: 要将图像用作目录图像,应使用高度不超过标题且宽度不超过 400 像素的图像(JPG、GIF 或 PNG)。 + hint: 目录图像应使用图像(JPG、GIF 或 PNG)。图片的宽度不应超过高度的 2 倍。 facet_panel_background_color: hint: 适用于某些主题中工作页面上的分面和附加节标题。 facet_panel_text_color: @@ -209,12 +220,10 @@ zh: fonts: 字形 themes: 主题 sidebar: - account: 帐户 accounts: 帐号 activity_summary: 活动摘要 labels: 标签 manage_groups: 管理组 - repository_activity: 存储库活动 system_status: 系统状态 users: activate: @@ -234,6 +243,9 @@ zh: active: 活性 pending: 有待 status_label: 状态 + roles: + remove: + confirmation: 您确定要从用户“%{user}”中删除角色“%{role}”吗? permissions: collections: cannot: From d2649f93e3316d70c534e58403c7cb77705fec05 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Tue, 19 Dec 2023 13:56:57 -0500 Subject: [PATCH 04/20] =?UTF-8?q?=F0=9F=8E=81=20Contribute=20back=20work?= =?UTF-8?q?=20from=20PALNI/PALCI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to Issues: - https://github.com/samvera/hyku/issues/1815 - https://github.com/scientist-softserv/palni-palci/issues/951 Related to Pull Requests: - https://github.com/scientist-softserv/palni-palci/pull/952 --- .../hyrax/collection_presenter_decorator.rb | 19 +++++++ .../collection_presenter_decorator_spec.rb | 52 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 spec/presenters/hyrax/collection_presenter_decorator_spec.rb diff --git a/app/presenters/hyrax/collection_presenter_decorator.rb b/app/presenters/hyrax/collection_presenter_decorator.rb index c348e5888..1adb737b1 100644 --- a/app/presenters/hyrax/collection_presenter_decorator.rb +++ b/app/presenters/hyrax/collection_presenter_decorator.rb @@ -96,6 +96,25 @@ def collection_featurable? user_can_feature_collection? && solr_document.public? end + ## + # OVERRIDE to handle search_only tenant's not having access to the collection type badge from + # the document's home tenant. + # + # @return [String] + # + # @see https://github.com/scientist-softserv/palni-palci/issues/951 + # @see https://github.com/samvera/hyku/issues/1815 + def collection_type_badge + return "" unless Site.account&.present? + return "" if Site.account.search_only? + + super + rescue ActiveRecord::RecordNotFound + # This is a fail-safe if we deleted the underlying Hyrax::CollectionType but have not yet + # cleaned up the SOLR records. + "" + end + def display_feature_collection_link? collection_featurable? && FeaturedCollection.can_create_another? && !collection_featured? end diff --git a/spec/presenters/hyrax/collection_presenter_decorator_spec.rb b/spec/presenters/hyrax/collection_presenter_decorator_spec.rb new file mode 100644 index 000000000..d5c795cbd --- /dev/null +++ b/spec/presenters/hyrax/collection_presenter_decorator_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Hyrax::CollectionPresenter do + describe '#collection_type_badge' do + subject { presenter.collection_type_badge } + + # We're decorating an alternate base class so that we don't need the full pre-amble for testing + # our decoration. In other words, let's trust Hyrax::CollectionPresenter's specs for the + # "super" method call. + let(:base_class) do + Class.new do + def collection_type_badge + "" + end + prepend Hyrax::CollectionPresenterDecorator + end + end + let(:presenter) { base_class.new } + + before { allow(Site).to receive(:account).and_return(account) } + + context 'when the Site.account is nil' do + let(:account) { nil } + + it { is_expected.to eq("") } + end + + context 'when the Site.account is search_only' do + let(:account) { FactoryBot.build(:account, search_only: true) } + + it { is_expected.to eq("") } + end + + context 'when the Site.account is NOT search_only' do + let(:account) { FactoryBot.build(:account, search_only: false) } + + it { is_expected.to start_with(" Date: Mon, 8 Jan 2024 11:49:59 -0500 Subject: [PATCH 05/20] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Account=20for=20obse?= =?UTF-8?q?rved=20customizations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dear reviewer, put on your reading glasses. This commit looks at the newly refactored `Hyrax::IiifAv::DisplaysContentDecorator` as well as the current state of [PALS's Hyrax::IiifAv::DisplaysContentDecorator][1] and attempts to account for the variances between the two by introducing configurations. Yes, we could port this to the hyrax-iiif_av gem, but for now that would not solve the underlying issue of how we've been handling things. Why the Hyku::Application class attribute? Because the decorator is being mixed into a module, which does not response to `.class_attribute` methods. [1]: https://github.com/scientist-softserv/palni-palci/blob/8754556c0225ce9f04674c1ffac6403586fd65f4/app/presenters/concerns/hyrax/iiif_av/displays_content_decorator.rb --- .../iiif_av/displays_content_decorator.rb | 88 ++++++++++++++++++- config/application.rb | 24 ++++- spec/config/application_spec.rb | 5 ++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/app/presenters/concerns/hyrax/iiif_av/displays_content_decorator.rb b/app/presenters/concerns/hyrax/iiif_av/displays_content_decorator.rb index 00ff7cd0d..23a3cb8dd 100644 --- a/app/presenters/concerns/hyrax/iiif_av/displays_content_decorator.rb +++ b/app/presenters/concerns/hyrax/iiif_av/displays_content_decorator.rb @@ -16,12 +16,98 @@ def current_ability defined?(super) ? super : @ability end - Request = Struct.new(:base_url, keyword_init: true) def request Request.new(base_url: hostname) end + + private + + def image_content + return nil unless latest_file_id + url = Hyrax.config.iiif_image_url_builder.call( + latest_file_id, + request.base_url, + Hyrax.config.iiif_image_size_default, + solr_document.mime_type + ) + + # Serving up only prezi 3 + image_content_v3(url) + end + + ## + # @note In the case where we have stream_urls, we'll assume the URL is correct. In the case + # where we're deferring to the document, we'll use {Hyku::Application.iiif_video_labels_and_mime_types} + def video_content + # @see https://github.com/samvera-labs/iiif_manifest + streams = stream_urls + if streams.present? + streams.collect { |label, url| video_display_content(url, label) } + else + Hyku::Application.iiif_video_labels_and_mime_types.map do |label, mime_type| + url = Hyku::Application.iiif_video_url_builder.call(document: solr_document, label:, host: request.base_url) + video_display_content(url, label, mime_type:) + end + end + end + + # rubocop:disable Metrics/MethodLength + def video_display_content(url, label = '', mime_type: solr_document.mime_type) + width = solr_document.width&.try(:to_i) || 320 + height = solr_document.height&.try(:to_i) || 240 + duration = conformed_duration_in_seconds + IIIFManifest::V3::DisplayContent.new( + url, + label:, + width:, + height:, + duration:, + type: 'Video', + format: mime_type + ) + end + + ## + # @note In the case where we have stream_urls, we'll assume the URL is correct. In the case + # where we're deferring to the document, we'll use {Hyku::Application.iiif_audio_labels_and_mime_types} + def audio_content + streams = stream_urls + if streams.present? + streams.collect { |label, url| audio_display_content(url, label) } + else + Hyku::Application.iiif_audio_labels_and_mime_types.map do |label, mime_type| + url = Hyku::Application.iiif_audio_url_builder.call(document: solr_document, label:, host: request.base_url) + audio_display_content(url, label, mime_type:) + end + end + end + + def audio_display_content(url, label = '', mime_type: solr_document.mime_type) + duration = conformed_duration_in_seconds + IIIFManifest::V3::DisplayContent.new( + url, + label:, + duration:, + type: 'Sound', + format: mime_type + ) + end + + def conformed_duration_in_seconds + if Array(solr_document.duration)&.first&.count(':') == 3 + # takes care of milliseconds like ["0:0:01:001"] + Time.zone.parse(Array(solr_document.duration).first.sub(/.*\K:/, '.')).seconds_since_midnight + elsif Array(solr_document.duration)&.first&.include?(':') + # if solr_document.duration evaluates to something like ["0:01:00"] which will get converted to seconds + Time.zone.parse(Array(solr_document.duration).first).seconds_since_midnight + else + # handles cases if solr_document.duration evaluates to something like ['25 s'] + Array(solr_document.duration).first.try(:to_f) + end || + 400.0 + end end end end diff --git a/config/application.rb b/config/application.rb index 8cbfb468d..611a9cbbc 100644 --- a/config/application.rb +++ b/config/application.rb @@ -92,8 +92,30 @@ class Application < Rails::Application # Hyrax::Engine.routes.url_helpers.download_url(document, host:, protocol: 'https') # end class_attribute :iiif_video_url_builder, - default: ->(document:, label:, host:) { Hyrax::IiifAv::Engine.routes.url_helpers.iiif_av_content_url(document.id, label:, host:) } + default: ->(document:, label:, host:) { Hyrax::IiifAv::Engine.routes.url_helpers.iiif_av_content_url(document.id, label:, host:) } + ## + # @!attribute iiif_audio_url_builder [r|w] + # @param document [SolrDocument] + # @param label [String] + # @param host [String] (e.g. samvera.org) + # @return [String] the fully qualified URL. + # @see Hyrax::IiifAv::DisplaysContentDecorator + # + # @example + # # The below example will build a URL taht will download directly from Hyrax as the + # # audio resource. This is a hack to address the processing times of audio derivatives; + # # namely in certain setups/configurations of Hyku, audio processing is laggy. + # # + # # The draw back of using this method is that we're pointing to the original audio file. + # # This is acceptable if the original file has already been processed out of band (e.g. + # # before uploading to Hyku/Hyrax). When we're dealing with a raw audio, this may not + # # be ideal for streaming. + # Hyrax::IiifAv::DisplaysContent.iiif_audio_url_builder = ->(document:, label:, host:) do + # Hyrax::Engine.routes.url_helpers.download_url(document, host:, protocol: 'https') + # end + class_attribute :iiif_audio_url_builder, + default: ->(document:, label:, host:) { Hyrax::IiifAv::Engine.routes.url_helpers.iiif_av_content_url(document.id, label:, host:) } # @!endgroup Class Attributes # Add this line to load the lib folder first because we need diff --git a/spec/config/application_spec.rb b/spec/config/application_spec.rb index b380bcaa9..7539b7214 100644 --- a/spec/config/application_spec.rb +++ b/spec/config/application_spec.rb @@ -39,4 +39,9 @@ subject { described_class.iiif_video_url_builder } it { is_expected.to be_a(Proc) } end + + describe '.iiif_audio_url_builder' do + subject { described_class.iiif_audio_url_builder } + it { is_expected.to be_a(Proc) } + end end From 99a97339bbb42508a43f24094222e200c6c92985 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 12:00:14 -0500 Subject: [PATCH 06/20] =?UTF-8?q?=F0=9F=A7=B9=20Appeasing=20Rubocop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/hyrax/homepage_controller.rb | 1 - app/helpers/application_helper.rb | 2 +- app/helpers/pdf_js_helper.rb | 2 +- app/helpers/shared_search_helper.rb | 2 +- .../hyrax/file_set_indexer_decorator.rb | 36 ++++++++++--------- app/models/concerns/pdf_behavior.rb | 14 ++++---- .../hyrax/search_service_decorator.rb | 6 ++++ app/services/iiif_print/tenant_config.rb | 4 +-- app/services/roles_service.rb | 4 +++ config/application.rb | 2 +- .../hyrax/file_set_indexer_decorator_spec.rb | 4 +-- 11 files changed, 44 insertions(+), 33 deletions(-) diff --git a/app/controllers/hyrax/homepage_controller.rb b/app/controllers/hyrax/homepage_controller.rb index f908b26fa..afaec85a2 100644 --- a/app/controllers/hyrax/homepage_controller.rb +++ b/app/controllers/hyrax/homepage_controller.rb @@ -57,7 +57,6 @@ def index # override hyrax v2.9.0 added for facets on homepage - Adding Themes (@response, @document_list) = search_results(params) - respond_to do |format| format.html { store_preferred_view } format.rss { render layout: false } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bb474f52c..28fcb3869 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,7 +10,7 @@ module ApplicationHelper include HykuKnapsack::ApplicationHelper def label_for(term:, record_class: nil) - locale_for(type: 'labels', term: term, record_class: record_class) + locale_for(type: 'labels', term:, record_class:) end def hint_for(term:, record_class: nil) diff --git a/app/helpers/pdf_js_helper.rb b/app/helpers/pdf_js_helper.rb index 8ba877fa9..ddfcb2989 100644 --- a/app/helpers/pdf_js_helper.rb +++ b/app/helpers/pdf_js_helper.rb @@ -7,7 +7,7 @@ def pdf_js_url(path) def pdf_file_set_presenter(presenter) # currently only supports one pdf per work, falls back to the first pdf file set in ordered members - representative_presenter(presenter) || presenter.file_set_presenters.select(&:pdf?).first + representative_presenter(presenter) || presenter.file_set_presenters.find(&:pdf?) end def representative_presenter(presenter) diff --git a/app/helpers/shared_search_helper.rb b/app/helpers/shared_search_helper.rb index 6313fb810..7d86b9444 100644 --- a/app/helpers/shared_search_helper.rb +++ b/app/helpers/shared_search_helper.rb @@ -17,7 +17,7 @@ def generate_work_url(model, request) id = model["id"] end request_params = %i[protocol host port].map { |method| ["request_#{method}".to_sym, request.send(method)] }.to_h - url = get_url(id: id, request: request_params, account_cname: account_cname, has_model: has_model) + url = get_url(id:, request: request_params, account_cname:, has_model:) # pass search query params to work show page params[:q].present? ? "#{url}?q=#{params[:q]}" : url diff --git a/app/indexers/hyrax/file_set_indexer_decorator.rb b/app/indexers/hyrax/file_set_indexer_decorator.rb index 0fdd59eeb..353352d18 100644 --- a/app/indexers/hyrax/file_set_indexer_decorator.rb +++ b/app/indexers/hyrax/file_set_indexer_decorator.rb @@ -14,25 +14,27 @@ def generate_solr_document private - def pdf_text - return unless object.pdf? - return unless object.original_file&.content.is_a? String - - begin - text = IO.popen(['pdftotext', '-', '-'], 'r+b') do |pdftotext| - pdftotext.write(object.original_file.content) - pdftotext.close_write - pdftotext.read - end - - text.tr("\n", ' ') - .squeeze(' ') - .encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') # remove non-UTF-8 characters - rescue Errno::ENOENT => e - raise e unless e.message.include?("No such file or directory - pdftotext") - Rails.logger.warn("`pdfinfo' is not installed; unable to extract text from the PDF's content") + # rubocop:disable Metrics/MethodLength + def pdf_text + return unless object.pdf? + return unless object.original_file&.content.is_a? String + + begin + text = IO.popen(['pdftotext', '-', '-'], 'r+b') do |pdftotext| + pdftotext.write(object.original_file.content) + pdftotext.close_write + pdftotext.read end + + text.tr("\n", ' ') + .squeeze(' ') + .encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') # remove non-UTF-8 characters + rescue Errno::ENOENT => e + raise e unless e.message.include?("No such file or directory - pdftotext") + Rails.logger.warn("`pdfinfo' is not installed; unable to extract text from the PDF's content") end + end + # rubocop:enable Metrics/MethodLength end end diff --git a/app/models/concerns/pdf_behavior.rb b/app/models/concerns/pdf_behavior.rb index af88f8189..959dc57e8 100644 --- a/app/models/concerns/pdf_behavior.rb +++ b/app/models/concerns/pdf_behavior.rb @@ -29,12 +29,12 @@ module PdfBehavior private - # This is here so that the checkbox is checked by default - def set_default_show_pdf_viewer - self.show_pdf_viewer ||= '1' - end + # This is here so that the checkbox is checked by default + def set_default_show_pdf_viewer + self.show_pdf_viewer ||= '1' + end - def set_default_show_pdf_download_button - self.show_pdf_download_button ||= '1' - end + def set_default_show_pdf_download_button + self.show_pdf_download_button ||= '1' + end end diff --git a/app/services/hyrax/search_service_decorator.rb b/app/services/hyrax/search_service_decorator.rb index e83f090a4..0c5be21f6 100644 --- a/app/services/hyrax/search_service_decorator.rb +++ b/app/services/hyrax/search_service_decorator.rb @@ -4,6 +4,9 @@ module Hyrax module SearchServiceDecorator + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/CyclomaticComplexity + # rubocop:disable Metrics/PerceivedComplexity def search_results builder = search_builder.with(user_params) builder.page = user_params[:page] if user_params[:page] @@ -22,6 +25,9 @@ def search_results [response, response.documents] end end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/CyclomaticComplexity + # rubocop:enable Metrics/PerceivedComplexity end end diff --git a/app/services/iiif_print/tenant_config.rb b/app/services/iiif_print/tenant_config.rb index 96bbaf95c..889dc3e3c 100644 --- a/app/services/iiif_print/tenant_config.rb +++ b/app/services/iiif_print/tenant_config.rb @@ -66,7 +66,7 @@ def valid? %i[create_derivatives cleanup_derivatives].each do |method_name| define_method(method_name) do |*args| - raise LeakyAbstractionError.new(klass: self.class, method_name: method_name) unless use_iiif_print? + raise LeakyAbstractionError.new(klass: self.class, method_name:) unless use_iiif_print? iiif_print_service_instance.public_send(method_name, *args) end @@ -176,7 +176,7 @@ def iiif_viewer? def members_include_iiif_viewable? iiif_presentable_member_presenters.any? do |presenter| - iiif_media?(presenter: presenter) && current_ability.can?(:read, presenter.id) + iiif_media?(presenter:) && current_ability.can?(:read, presenter.id) end end diff --git a/app/services/roles_service.rb b/app/services/roles_service.rb index 4da81a5f7..f28dfda32 100644 --- a/app/services/roles_service.rb +++ b/app/services/roles_service.rb @@ -241,6 +241,7 @@ def perform end class CreateCollectionAccessesJob < Hyrax::ApplicationJob + # rubocop:disable Metrics/MethodLength def perform Collection.find_each do |c| pt = Hyrax::PermissionTemplate.find_or_create_by!(source_id: c.id) @@ -273,9 +274,11 @@ def perform c.reset_access_controls! if pt.access_grants.count != original_access_grants_count end end + # rubocop:enable Metrics/MethodLength end class CreateAdminSetAccessesJob < Hyrax::ApplicationJob + # rubocop:disable Metrics/MethodLength def perform AdminSet.find_each do |as| pt = Hyrax::PermissionTemplate.find_or_create_by!(source_id: as.id) @@ -308,6 +311,7 @@ def perform as.reset_access_controls! if pt.access_grants.count != original_access_grants_count end end + # rubocop:enable Metrics/MethodLength end class CreateCollectionTypeParticipantsJob < Hyrax::ApplicationJob diff --git a/config/application.rb b/config/application.rb index 611a9cbbc..d78f209bf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -215,7 +215,7 @@ def self.path_for(relative_path) Object.include(AccountSwitch) end - config.autoload_paths << "#{Rails.root}/app/controllers/api" + config.autoload_paths << Rails.root.join("app", "controllers", "api") # copies tinymce assets directly into public/assets config.tinymce.install = :copy diff --git a/spec/indexers/hyrax/file_set_indexer_decorator_spec.rb b/spec/indexers/hyrax/file_set_indexer_decorator_spec.rb index 4f2fd39fe..470d24b40 100644 --- a/spec/indexers/hyrax/file_set_indexer_decorator_spec.rb +++ b/spec/indexers/hyrax/file_set_indexer_decorator_spec.rb @@ -7,8 +7,8 @@ let(:actor) { Hyrax::Actors::FileActor.new(file_set, relation, user) } let(:file_path) { File.join(fixture_path, 'pdf', 'archive.pdf') } let(:fixture) { fixture_file_upload(file_path, 'application/pdf') } - let(:huf) { Hyrax::UploadedFile.new(user: user, file_set_uri: file_set.uri, file: fixture) } - let(:io) { JobIoWrapper.new(file_set_id: file_set.id, user: user, uploaded_file: huf) } + let(:huf) { Hyrax::UploadedFile.new(user:, file_set_uri: file_set.uri, file: fixture) } + let(:io) { JobIoWrapper.new(file_set_id: file_set.id, user:, uploaded_file: huf) } let(:solr_document) { SolrDocument.find(file_set.id) } let!(:test_strategy) { Flipflop::FeatureSet.current.test! } From c7bbda6a4d7f2c5831c09120ee73cc716a6ded5b Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 12:07:53 -0500 Subject: [PATCH 07/20] Remove conflict in controller --- app/controllers/catalog_controller.rb | 10 ++++------ config/routes.rb | 4 ---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index 09d74a752..0fae9c8ef 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -89,12 +89,10 @@ def self.uploaded_field config.default_solr_params = { qt: "search", rows: 10, -<<<<<<< HEAD - qf: IiifPrint.config.metadata_fields.keys.map { |attribute| "#{attribute}_tesim" } - .join(' ') << " title_tesim description_tesim all_text_timv file_set_text_tsimv", # the first space character is necessary! -======= - qf: "title_tesim alternative_title_tesim description_tesim creator_tesim contributor_tesim related_url_tesim learning_resource_type_tesim education_level_tesim audience_tesim degree_name_tesim degree_discipline_tesim degree_grantor_tesim date_tesim table_of_contents_tesim rights_statement_tesim license_tesim rights_holder_tesim publisher_tesim identifier_tesim keyword_tesim subject_tesim language_tesim resource_type_tesim date_created_tesim date_uploaded_tesim date_modified_tesim accessibility_feature_tesim accessibility_hazard_tesim accessibility_summary_tesim format_tesim extent_tesim all_text_timv", ->>>>>>> pals-contribute-back-to-prime + qf: ( + IiifPrint.config.metadata_fields.keys.map { |attribute| "#{attribute}_tesim" } + + ["title_tesim", "description_tesim", "all_text_timv", "file_set_text_tsimv"] + ).join(' '), "hl": true, "hl.simple.pre": "", "hl.simple.post": "", diff --git a/config/routes.rb b/config/routes.rb index ce171f1f2..1a7e3b50b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,11 +78,7 @@ mount Qa::Engine => '/authorities' mount Blacklight::Engine => '/' -<<<<<<< HEAD mount BlacklightAdvancedSearch::Engine => '/' -======= - ->>>>>>> pals-contribute-back-to-prime mount Hyrax::Engine, at: '/' mount Bulkrax::Engine, at: '/' if ENV.fetch('HYKU_BULKRAX_ENABLED', 'true') == 'true' mount HykuKnapsack::Engine, at: '/' From 747601fe145d6ed24c3c55ac110d4b5d85ecf61a Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 13:03:37 -0500 Subject: [PATCH 08/20] =?UTF-8?q?=F0=9F=90=9B=20Add=20tests=20for=20all=20?= =?UTF-8?q?defined=20color=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyrax/forms/admin/appearance_decorator.rb | 23 ++++++++++--------- .../forms/admin/appearance_decorator_spec.rb | 11 ++++++++- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/forms/hyrax/forms/admin/appearance_decorator.rb b/app/forms/hyrax/forms/admin/appearance_decorator.rb index b87333a8f..cc03cb6a0 100644 --- a/app/forms/hyrax/forms/admin/appearance_decorator.rb +++ b/app/forms/hyrax/forms/admin/appearance_decorator.rb @@ -35,23 +35,24 @@ module AppearanceDecorator # @!attribute default_colors # @return [Hash] class_attribute :default_colors, default: { - 'header_and_footer_background_color' => '#3c3c3c', + 'active_tabs_background_color' => '#337ab7', + 'default_button_background_color' => '#ffffff', + 'default_button_border_color' => '#cccccc', + 'default_button_text_color' => '#333333', + 'facet_panel_background_color' => '#f5f5f5', + 'facet_panel_text_color' => '#333333', + 'footer_link_color' => '#ffebcd', + 'footer_link_hover_color' => '#ffffff', 'header_and_footer_text_color' => '#dcdcdc', + 'link_color' => '#2e74b2', + 'link_hover_color' => '#215480', 'navbar_background_color' => '#000000', + 'navbar_link_background_color' => '#375f8c', 'navbar_link_background_hover_color' => '#ffffff', 'navbar_link_text_color' => '#eeeeee', 'navbar_link_text_hover_color' => '#eeeeee', - 'link_color' => '#2e74b2', - 'link_hover_color' => '#215480', - 'footer_link_color' => '#ffebcd', - 'footer_link_hover_color' => '#ffffff', 'primary_button_hover_color' => '#286090', - 'default_button_background_color' => '#ffffff', - 'default_button_border_color' => '#cccccc', - 'default_button_text_color' => '#333333', - # 'active_tabs_background_color' => '#337ab7', - 'facet_panel_background_color' => '#f5f5f5', - 'facet_panel_text_color' => '#333333' + 'header_and_footer_background_color' => '#3c3c3c' } # @!endgroup Class Attributes end diff --git a/spec/forms/hyrax/forms/admin/appearance_decorator_spec.rb b/spec/forms/hyrax/forms/admin/appearance_decorator_spec.rb index 0fb32f2a1..7e479cbe1 100644 --- a/spec/forms/hyrax/forms/admin/appearance_decorator_spec.rb +++ b/spec/forms/hyrax/forms/admin/appearance_decorator_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' RSpec.describe Hyrax::Forms::Admin::Appearance, type: :decorator do + let(:instance) { described_class.new } describe '.default_fonts' do subject { described_class.default_fonts } @@ -26,8 +27,16 @@ end describe '#banner_image' do - subject { described_class.new.banner_image } + subject { instance.banner_image } it { is_expected.to be_a(Hyrax::AvatarUploader) } end + + described_class.instance_methods.grep(/_color$/).each do |color_method_name| + describe "##{color_method_name}" do + subject { instance.send(color_method_name) } + + it { is_expected.to match(/^#[0-9A-F]{6}/i) } + end + end end From 821cbf2e305d47cba4bae8fac623e65a93259afb Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 13:25:30 -0500 Subject: [PATCH 09/20] =?UTF-8?q?=F0=9F=90=9B=20Add=20placement=5Fclass=20?= =?UTF-8?q?for=20masthead=20rendering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change aligns with expectations in [Hyrax 5.0.0.rc1][1] [1]:https://github.com/samvera/hyrax/blob/966951ffaa72524e4a775f8a198bd51a47ece7d9/app/views/layouts/hyrax/dashboard.html.erb#L12 --- app/views/layouts/hyrax/dashboard.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/hyrax/dashboard.html.erb b/app/views/layouts/hyrax/dashboard.html.erb index 5757dc589..7644cb574 100644 --- a/app/views/layouts/hyrax/dashboard.html.erb +++ b/app/views/layouts/hyrax/dashboard.html.erb @@ -11,7 +11,7 @@
    <%= link_to "Skip to Content", "#skip-to-content" %>
    - <%= render '/masthead' %> + <%= render '/masthead', placement_class: 'fixed-top' %> <%= content_for(:navbar) %>
    <%= render 'shared/ajax_modal' %> - \ No newline at end of file + From a559908147afa211c3c8c547f852004abb0dc83d Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 13:53:03 -0500 Subject: [PATCH 10/20] Removing unneeded file --- app/views/layouts/hyrax/dashboard.html.erb | 41 ---------------------- 1 file changed, 41 deletions(-) delete mode 100644 app/views/layouts/hyrax/dashboard.html.erb diff --git a/app/views/layouts/hyrax/dashboard.html.erb b/app/views/layouts/hyrax/dashboard.html.erb deleted file mode 100644 index 7644cb574..000000000 --- a/app/views/layouts/hyrax/dashboard.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -<%# OVERRIDE from Hyrax 3.6.0 to hide broken flash messages on the dashboard edit work pages %> - - - - - <%= render partial: 'layouts/head_tag_content' %> - <%= content_for(:head) %> - - - -
    - <%= link_to "Skip to Content", "#skip-to-content" %> -
    - <%= render '/masthead', placement_class: 'fixed-top' %> - <%= content_for(:navbar) %> -
    - -
    - <%# OVERRIDE here to hide broken flash messages on the dashboard edit work pages. %> - <%# regex includes only the paths for works since this is the only known place flash messages are broken. %> - <%= render '/flash_msg' %> - <%= render_breadcrumbs builder: Hyrax::BootstrapBreadcrumbsBuilder %> - <% if content_for?(:page_header) %> -
    -
    - <%= yield(:page_header) %> -
    -
    - <% end %> - - - <%= content_for?(:content) ? yield(:content) : yield %> - -
    - -
    - <%= render 'shared/ajax_modal' %> - - From f2c6b8911ca1708dcbaa82ee025c72966dc147dc Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 13:53:25 -0500 Subject: [PATCH 11/20] Resolving menu structure for site configuration --- .../dashboard/sidebar/_configuration.html.erb | 73 +++++++------------ 1 file changed, 25 insertions(+), 48 deletions(-) diff --git a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb index c39b83b17..823225271 100644 --- a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb +++ b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb @@ -1,85 +1,62 @@ -<%# OVERRIDE Hyrax v5.0.0rc2 add accoun settings and title attributes %> +<%# OVERRIDE Hyrax v5.0.0rc2 add account settings and title attributes %> <% if menu.show_configuration? %> <% if can? :manage, Site %> <% end %> - <% if Flipflop.show_workflow_roles_menu_item_in_admin_dashboard_sidebar? && can?(:manage, Sipity::WorkflowResponsibility) %> - <%= menu.nav_link(hyrax.admin_workflow_roles_path, - class: "nav-link", - title: t('hyrax.admin.sidebar.workflow_roles')) do %> - <%= t('hyrax.admin.sidebar.workflow_roles') %> - <% end %> - <% end %> <%= render 'hyrax/dashboard/sidebar/menu_partials', menu: menu, section: :configuration %> - <% if can?(:update, RolesService) %> - <%= menu.nav_link(main_app.admin_roles_service_jobs_path) do %> - <%= t('hyrax.admin.sidebar.roles_service_jobs') %> - <% end %> - <% end %> <% end %> From b2334faf73cbf1615c3f53416b39e5cd5ca1aff0 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 14:37:03 -0500 Subject: [PATCH 12/20] Applying changes in main to this work --- app/controllers/catalog_controller.rb | 2 +- app/jobs/reindex_works_job.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index 0fae9c8ef..de831c88e 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -92,7 +92,7 @@ def self.uploaded_field qf: ( IiifPrint.config.metadata_fields.keys.map { |attribute| "#{attribute}_tesim" } + ["title_tesim", "description_tesim", "all_text_timv", "file_set_text_tsimv"] - ).join(' '), + ).uniq.join(' '), "hl": true, "hl.simple.pre": "", "hl.simple.post": "", diff --git a/app/jobs/reindex_works_job.rb b/app/jobs/reindex_works_job.rb index 5d9c50cfc..e8ee3adc7 100644 --- a/app/jobs/reindex_works_job.rb +++ b/app/jobs/reindex_works_job.rb @@ -5,7 +5,7 @@ def perform(work = nil) if work.present? work.update_index else - Hyrax.config.registered_curation_concern_types.each do |work_type| + Site.instance.available_works.each do |work_type| work_type.constantize.find_each do |w| ReindexItemJob.perform_later(w) end From 0fbde5004ceaaadffd124e5b3296c806fc5f50fb Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 15:35:49 -0500 Subject: [PATCH 13/20] Fixing locale_for tests to reflect I18n behavior --- app/helpers/application_helper.rb | 20 +++++++++++--------- spec/helpers/application_helper_spec.rb | 7 +++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 28fcb3869..b29350605 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -20,18 +20,20 @@ def hint_for(term:, record_class: nil) end def locale_for(type:, term:, record_class:) - @term = term.to_s - @record_class = record_class.to_s.downcase - work_or_collection = @record_class == 'collection' ? 'collection' : 'defaults' - default_locale = t("simple_form.#{type}.#{work_or_collection}.#{@term}").html_safe - locale = t("hyrax.#{@record_class}.#{type}.#{@term}").html_safe - - return default_locale if missing_translation(locale) - - locale + term = term.to_s + record_class = record_class.to_s.downcase + work_or_collection = record_class == 'collection' ? 'collection' : 'defaults' + locale = t("hyrax.#{record_class}.#{type}.#{term}") + + if missing_translation(locale) + (t("simple_form.#{type}.#{work_or_collection}.#{term}") || term.titleize) .html_safe + else + locale.html_safe + end end def missing_translation(value, _options = {}) + return true if value == false return true if value.try(:false?) false end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 9b684f526..86370652e 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -10,4 +10,11 @@ expect(helper.markdown(bold)).to eq("

    bold

    \n") end end + + describe '#local_for' do + context 'when term is missing' do + subject { helper.locale_for(type: 'labels', record_class: "account", term: :very_much_missing) } + it { is_expected.to be_a(String) } + end + end end From 5894b93010aa119df8d3fb410d9988107f0718c5 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 15:39:48 -0500 Subject: [PATCH 14/20] =?UTF-8?q?=F0=9F=90=9B=20Use=20non-deprecated=20and?= =?UTF-8?q?=20non-removed=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `member_presenters_for` was removed in https://github.com/samvera/hyrax/commit/eeb3c3e888f87809b5f55287f1d95302d61d6ba8; the deprecation advice prior to removal was to use `member_presenters` Related to: - https://github.com/samvera/hyrax/pull/6203 --- app/helpers/application_helper.rb | 2 +- app/services/iiif_print/tenant_config.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b29350605..5a8abb371 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -26,7 +26,7 @@ def locale_for(type:, term:, record_class:) locale = t("hyrax.#{record_class}.#{type}.#{term}") if missing_translation(locale) - (t("simple_form.#{type}.#{work_or_collection}.#{term}") || term.titleize) .html_safe + (t("simple_form.#{type}.#{work_or_collection}.#{term}") || term.titleize) .html_safe else locale.html_safe end diff --git a/app/services/iiif_print/tenant_config.rb b/app/services/iiif_print/tenant_config.rb index 889dc3e3c..183a8200b 100644 --- a/app/services/iiif_print/tenant_config.rb +++ b/app/services/iiif_print/tenant_config.rb @@ -191,7 +191,7 @@ def members_include_iiif_viewable? def iiif_presentable_member_presenters if TenantConfig.use_iiif_print? presentable_member_ids = Array.wrap(solr_document.try(:file_set_ids) || solr_document.try(:[], 'file_set_ids_ssim')) - member_presenters_for(presentable_member_ids) + member_presenters(presentable_member_ids) else file_set_presenters end From 6b17b5cab2f8b15c0508625df0034236205eb377 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 16:14:48 -0500 Subject: [PATCH 15/20] Fixing roles service --- app/services/roles_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/roles_service.rb b/app/services/roles_service.rb index f28dfda32..e7f418b10 100644 --- a/app/services/roles_service.rb +++ b/app/services/roles_service.rb @@ -271,7 +271,7 @@ def perform agent_id: 'collection_reader' ) - c.reset_access_controls! if pt.access_grants.count != original_access_grants_count + pt.reset_access_controls_for(collection: c) if pt.access_grants.count != original_access_grants_count end end # rubocop:enable Metrics/MethodLength @@ -308,7 +308,7 @@ def perform agent_id: 'work_editor' ) - as.reset_access_controls! if pt.access_grants.count != original_access_grants_count + pt.reset_access_controls_for(collection: as) if pt.access_grants.count != original_access_grants_count end end # rubocop:enable Metrics/MethodLength From ad0d56bc176d434d3b3d8f900db99c277892d4ff Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Mon, 8 Jan 2024 16:17:24 -0500 Subject: [PATCH 16/20] Removing file that came along for the ride --- .../hyrax/my/works_controller_spec.rb | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 spec/controllers/hyrax/my/works_controller_spec.rb diff --git a/spec/controllers/hyrax/my/works_controller_spec.rb b/spec/controllers/hyrax/my/works_controller_spec.rb deleted file mode 100644 index 52874d159..000000000 --- a/spec/controllers/hyrax/my/works_controller_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Hyrax::My::WorksController, type: :controller do - describe "#configure_facets" do - subject { controller.blacklight_config.sort_fields.keys } - - let(:expected_sort_fields) do - [ - "date_uploaded_dtsi desc", - "date_uploaded_dtsi asc", - "date_modified_dtsi desc", - "date_modified_dtsi asc", - "system_create_dtsi desc", - "system_create_dtsi asc", - "depositor_ssi asc, title_ssi asc", - "depositor_ssi desc, title_ssi desc", - "creator_ssi asc, title_ssi asc", - "creator_ssi desc, title_ssi desc" - ] - end - - it "configures the custom sort fields" do - expect(subject).to match_array(expected_sort_fields) - end - end -end From b8d7a2e14fe45f91634995407e16b62c50b83d89 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Tue, 9 Jan 2024 09:08:57 -0500 Subject: [PATCH 17/20] =?UTF-8?q?=F0=9F=90=9B=20Restore=20translation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The translation should be plural --- app/views/hyrax/dashboard/sidebar/_configuration.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb index 823225271..41713fd3c 100644 --- a/app/views/hyrax/dashboard/sidebar/_configuration.html.erb +++ b/app/views/hyrax/dashboard/sidebar/_configuration.html.erb @@ -5,8 +5,8 @@ <% if can? :manage, Site %>