Merge pull request #6500 from LevityNL/master
[civicrm-core.git] / ang / crmUi.js
index 312e921cdd9ddd811762aa35ffd9cdc1dffa6b51..f80ce39f18b6e6eafe391e89f4fb119d7e2f13d8 100644 (file)
@@ -6,7 +6,7 @@
   angular.module('crmUi', [])
 
     // example <div crm-ui-accordion crm-title="ts('My Title')" crm-collapsed="true">...content...</div>
-    // WISHLIST: crmCollapsed should support two-way/continous binding
+    // WISHLIST: crmCollapsed should support two-way/continuous binding
     .directive('crmUiAccordion', function() {
       return {
         scope: {
 
     // Display an HTML blurb inside an IFRAME.
     // example: <iframe crm-ui-iframe="getHtmlContent()"></iframe>
+    // example:  <iframe crm-ui-iframe crm-ui-iframe-src="getUrl()"></iframe>
     .directive('crmUiIframe', function ($parse) {
       return {
         scope: {
-          crmUiIframe: '@' // expression which evalutes to HTML content
+          crmUiIframeSrc: '@', // expression which evaluates to a URL
+          crmUiIframe: '@' // expression which evaluates to HTML content
         },
         link: function (scope, elm, attrs) {
           var iframe = $(elm)[0];
           iframe.setAttribute('frameborder', '0');
 
           var refresh = function () {
-            // var iframeHtml = '<html><head><base target="_blank"></head><body onload="parent.document.getElementById(\'' + iframe.id + '\').style.height=document.body.scrollHeight + \'px\'"><scr' + 'ipt type="text/javascript" src="https://gist.github.com/' + iframeId + '.js"></sc' + 'ript></body></html>';
-            var iframeHtml = scope.$parent.$eval(attrs.crmUiIframe);
-
-            var doc = iframe.document;
-            if (iframe.contentDocument) {
-              doc = iframe.contentDocument;
-            }
-            else if (iframe.contentWindow) {
-              doc = iframe.contentWindow.document;
+            if (attrs.crmUiIframeSrc) {
+              iframe.setAttribute('src', scope.$parent.$eval(attrs.crmUiIframeSrc));
             }
+            else {
+              var iframeHtml = scope.$parent.$eval(attrs.crmUiIframe);
+
+              var doc = iframe.document;
+              if (iframe.contentDocument) {
+                doc = iframe.contentDocument;
+              }
+              else if (iframe.contentWindow) {
+                doc = iframe.contentWindow.document;
+              }
 
-            doc.open();
-            doc.writeln(iframeHtml);
-            doc.close();
+              doc.open();
+              doc.writeln(iframeHtml);
+              doc.close();
+            }
           };
 
           // If the iframe is in a dialog, respond to resize events
     // Example:
     //   <a ng-click="$broadcast('my-insert-target', 'some new text')>Insert</a>
     //   <textarea crm-ui-insert-rx='my-insert-target'></textarea>
-    // TODO Consider ways to separate the plain-text/rich-text implementations
     .directive('crmUiInsertRx', function() {
       return {
         link: function(scope, element, attrs) {
           scope.$on(attrs.crmUiInsertRx, function(e, tokenName) {
-            var id = element.attr('id');
-            if (CKEDITOR.instances[id]) {
-              CKEDITOR.instances[id].insertText(tokenName);
-              $(element).select2('close').select2('val', '');
-              CKEDITOR.instances[id].focus();
-            }
-            else {
-              var crmForEl = $('#' + id);
-              var origVal = crmForEl.val();
-              var origPos = crmForEl[0].selectionStart;
-              var newVal = origVal.substring(0, origPos) + tokenName + origVal.substring(origPos, origVal.length);
-              crmForEl.val(newVal);
-              var newPos = (origPos + tokenName.length);
-              crmForEl[0].selectionStart = newPos;
-              crmForEl[0].selectionEnd = newPos;
-
-              $(element).select2('close').select2('val', '');
-              crmForEl.triggerHandler('change');
-              crmForEl.focus();
-            }
+            CRM.wysiwyg.insert(element, tokenName);
+            $(element).select2('close').select2('val', '');
+            CRM.wysiwyg.focus(element);
           });
         }
       };
       return {
         require: '?ngModel',
         link: function (scope, elm, attr, ngModel) {
-          var ck = CKEDITOR.replace(elm[0]);
-
-          if (ck) {
-            _.extend(ck.config, {
-              width: '94%',
-              height: '400',
-              filebrowserBrowseUrl: CRM.crmUi.browseUrl + '?cms=civicrm&type=files',
-              filebrowserImageBrowseUrl: CRM.crmUi.browseUrl + '?cms=civicrm&type=images',
-              filebrowserFlashBrowseUrl: CRM.crmUi.browseUrl + '?cms=civicrm&type=flash',
-              filebrowserUploadUrl: CRM.crmUi.uploadUrl + '?cms=civicrm&type=files',
-              filebrowserImageUploadUrl: CRM.crmUi.uploadUrl + '?cms=civicrm&type=images',
-              filebrowserFlashUploadUrl: CRM.crmUi.uploadUrl + '?cms=civicrm&type=flash',
-            });
-          }
 
+          var editor = CRM.wysiwyg.create(elm);
           if (!ngModel) {
             return;
           }
 
           if (attr.ngBlur) {
-            ck.on('blur', function(){
-              $timeout(function(){
+            $(elm).on('blur', function() {
+              $timeout(function() {
                 scope.$eval(attr.ngBlur);
               });
             });
           }
 
-          ck.on('pasteState', function () {
-            scope.$apply(function () {
-              ngModel.$setViewValue(ck.getData());
-            });
-          });
-
-          ck.on('insertText', function () {
-            $timeout(function () {
-              ngModel.$setViewValue(ck.getData());
+          // CRM-16445 - When one inserts an image, none of these events seem to fire at the right time:
+          // afterCommandExec, afterInsertHtml, afterPaste, afterSetData, change, insertElement,
+          // insertHtml, insertText, pasteState. It seems that 'pasteState' is the general equivalent of
+          // what 'change' should be, except (in the case of image insertion) it fires too soon.
+          // The 'key' event is needed to detect changes in "Source" mode.
+          var debounce = null;
+          angular.forEach(['key', 'pasteState'], function(evName){
+            ck.on(evName, function(evt) {
+              $timeout.cancel(debounce);
+              debounce = $timeout(function() {
+                ngModel.$setViewValue(ck.getData());
+              }, 50);
             });
           });
 
-          ngModel.$render = function (value) {
-            ck.setData(ngModel.$viewValue);
+          ngModel.$render = function(value) {
+            CRM.wysiwyg.setVal(elm, ngModel.$viewValue);
           };
         }
       };