1/* 2 * 3 * ADOBE CONFIDENTIAL 4 * __________________ 5 * 6 * Copyright 2012 Adobe Systems Incorporated 7 * All Rights Reserved. 8 * 9 * NOTICE: All information contained herein is, and remains 10 * the property of Adobe Systems Incorporated and its suppliers, 11 * if any. The intellectual and technical concepts contained 12 * herein are proprietary to Adobe Systems Incorporated and its 13 * suppliers and are protected by trade secret or copyright law. 14 * Dissemination of this information or reproduction of this material 15 * is strictly forbidden unless prior written permission is obtained 16 * from Adobe Systems Incorporated. 17 */ 18 19var SCFValidator = function(context, flag) { 20 var _self = this; 21 this.items = []; 22 if (context !== null) { 23 this.context = context; 24 } else { 25 this.context = $(document.body); 26 } 27 this.batch_processing = false; 28 var _batchProcessing = $(this.context).attr("data-scf-validator-batch-processing"); 29 if (typeof(_batchProcessing) !== "undefined" && _batchProcessing === "true") { 30 this.batch_processing = true; 31 } 32 33 this.elements = $('[data-scf-validator]'); 34 $.each(this.elements, function(key, value) { 35 if ($($(value).closest("form")).is(_self.context)) { 36 _self.items.push(new SCFValidatorItem(value, _self.batch_processing)); 37 } 38 }); 39}; 40SCFValidator.prototype.validate = function() { 41 var _elements = []; 42 $.each(this.items, function(key, value) { 43 _elements.push(value.validate()); 44 }); 45 var _passed = function(el, index, array) { 46 return el === true; 47 }; 48 return _elements.every(_passed); 49}; 50SCFValidator.prototype.reset = function() { 51 $.each(this.items, function(key, value) { 52 value.reset(); 53 }); 54}; 55 56/* 57 * 58 * ADOBE CONFIDENTIAL 59 * __________________ 60 * 61 * Copyright 2012 Adobe Systems Incorporated 62 * All Rights Reserved. 63 *
64 * NOTICE: All information contained herein is, and remains 65 * the property of Adobe Systems Incorporated and its suppliers, 66 * if any. The intellectual and technical concepts contained 67 * herein are proprietary to Adobe Systems Incorporated and its 68 * suppliers and are protected by trade secret or copyright law. 69 * Dissemination of this information or reproduction of this material 70 * is strictly forbidden unless prior written permission is obtained 71 * from Adobe Systems Incorporated. 72 */ 73var SCFValidatorItem = function(element, batch_processing) { 74 var _self = this; 75 this.element = element; 76 var _config = $(this.element).data("scf-validator"); 77 78 if (baseRules[_config.validation] !== null) { 79 this.validation = baseRules[_config.validation].regexp; 80 this.message = baseRules[_config.validation].text; 81 } else { 82 this.validation = baseRules["default"].regexp; 83 this.message = baseRules["default"].text; 84 } 85 86 this.callbacks = { 87 init: null, 88 success: null, 89 error: null, 90 complete: null 91 }; 92 93 this.evt = "change"; 94 this.resetevt = "focus"; 95 this.notification = "highlight"; 96 this.notification_str = ""; 97 this.error_element = null; 98 this.anchor_element = null; //Anchor Element is used to attach error/info message to. By default AE is the element, message will be attached next to it 99 this.error_css = null; 100 101 if (typeof(_config.evt) !== "undefined") { 102 this.evt = _config.evt; 103 } 104 if (typeof(_config.resetevt) !== "undefined") { 105 this.resetevt = _config.resetevt; 106 } 107 if (typeof(_config.notification) !== "undefined") { 108 this.notification = _config.notification; 109 } 110 //new callbacks impl 111 if (typeof(_config.callbacks) !== "undefined" && typeof(_config.callbacks) === "object") { 112 this.callbacks = _config.callbacks; 113 } 114 //calback on init 115 if (this.callbacks.init !== null) { 116 window[this.callbacks.init].call(); 117 } 118 if (typeof(this.notification) === "object") { 119 this.anchor_element = $("#" + this.notification.anchor); // TODO check for string/oject, then use obj or add $() 120 this.error_css = this.notification.css; 121 this.notification_str = "custom"; 122 } else { 123 this.anchor_element = this.element; 124 this.notification_str = this.notification; 125 this.error_css = "scf-validation-highlight-error"; 126 } 127 $(this.element).on(_self.resetevt, function() { 128 _self.reset(); 129 }); 130 if (batch_processing === false) { 131 $(this.element).on(_self.evt, function() { 132 if (_self.validate()) { 133 if (_self.callbacks.success !== null) { 134 window[_self.callbacks.success].call(); 135 } 136 } else { 137 if (_self.callbacks.error !== null) { 138 window[_self.callbacks.error].call(); 139 } 140 } 141 if (_self.callbacks.complete !== null) { 142 window[_self.callbacks.complete].call(); 143 } 144 }); 145 } 146 return this; 147}; 148SCFValidatorItem.prototype.validate = function() { 149 if ($(this.element).hasClass(this.error_css)) { 150 this.reset(); 151 } 152 var _value = $(this.element).val(); 153 var _patt = new RegExp(this.validation); 154 if (_patt.test(_value)) { 155 return true; 156 } 157 $(this.element).addClass(this.error_css); 158 this.error_element = $('<div class="alert alert-danger">' + this.message + '</div>'); 159 160 switch (this.notification_str) { 161 case "highlight": 162 $(this.error_element).insertAfter($(this.anchor_element)); 163 break; 164 case "popup": 165 alert(this.message); 166 break; 167 case "custom": 168 $(this.error_element).appendTo($(this.anchor_element)); 169 break; 170 default: 171 alert("Validation error message"); 172 } 173 return false; 174}; 175SCFValidatorItem.prototype.reset = function() { 176 $(this.element).removeClass(this.error_css); 177 if (this.error_element !== null) { 178 $(this.error_element).remove(); 179 } 180}; 181 182/* 183 * 184 * ADOBE CONFIDENTIAL 185 * __________________ 186 * 187 * Copyright 2012 Adobe Systems Incorporated 188 * All Rights Reserved. 189 *
190 * NOTICE: All information contained herein is, and remains 191 * the property of Adobe Systems Incorporated and its suppliers, 192 * if any. The intellectual and technical concepts contained 193 * herein are proprietary to Adobe Systems Incorporated and its 194 * suppliers and are protected by trade secret or copyright law. 195 * Dissemination of this information or reproduction of this material 196 * is strictly forbidden unless prior written permission is obtained 197 * from Adobe Systems Incorporated. 198 */ 199var baseRules = { 200 "default": { 201 "text": CQ.I18n.getMessage("Default match. Matches everything."), 202 "regexp": "[\s\S]*" 203 }, 204 "integer": { 205 "text": CQ.I18n.getMessage("Integer only"), 206 "regexp": /^\s*(\+|-)?\d+\s*$/ 207 }, 208 "text": { 209 "text": CQ.I18n.getMessage("Text only. No digits"), 210 "regexp": "^[a-zA-Z ]*$" 211 }, 212 "numbers": { 213 "text": CQ.I18n.getMessage("Numbers only. No special chars, or letters"), 214 "regexp": "^[0-9]*$" 215 }, 216 "messagesubject": { 217 "text": CQ.I18n.getMessage("Please enter min 2, max 10, numbers, letters and/or some characters"), 218 "regexp": "^([a-zA-Z0-9 \"~&|'!@#$%*()-_=+;?/{}]{2,10})$" 219 }, 220 "email": { 221 "text": CQ.I18n.getMessage("Please enter valid email address"), 222 "regexp": /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i 223 }, 224 "noempty": { 225 "text": CQ.I18n.getMessage("Cannot be empty"), 226 "regexp": /\S/ 227 } 228}; 229 230/* 231 * 232 * ADOBE CONFIDENTIAL 233 * __________________ 234 * 235 * Copyright 2012 Adobe Systems Incorporated 236 * All Rights Reserved. 237 *
238 * NOTICE: All information contained herein is, and remains 239 * the property of Adobe Systems Incorporated and its suppliers, 240 * if any. The intellectual and technical concepts contained 241 * herein are proprietary to Adobe Systems Incorporated and its 242 * suppliers and are protected by trade secret or copyright law. 243 * Dissemination of this information or reproduction of this material 244 * is strictly forbidden unless prior written permission is obtained 245 * from Adobe Systems Incorporated. 246 */ 247(function(Backbone, $CQ, _, Handlebars) { 248 "use strict"; 249 var SCF = { 250 VERSION: "0.0.1", 251 Views: {}, 252 Models: {}, 253 Collections: {}, 254 config: { 255 urlRoot: "" 256 }, 257 constants: { 258 SOCIAL_SELECTOR: ".social", 259 JSON_EXT: ".json", 260 URL_EXT: ".social.json", 261 TRANSLATE_URL_EXT: ".social.translate.json", 262 ANALYTICS_BASE_RESOURCE_TYPE: "social/commons/components/analyticsbase" 263 }, 264 Components: {}, 265 loadedComponents: {}, 266 templates: {}, 267 fieldManagers: {}, 268 DEBUG: 1, 269 INFO: 2, 270 WARN: 3, 271 ERROR: 4 272 }; 273 SCF.LOG_LEVEL = SCF.INFO; 274 var _logger = { 275 debug: function() { 276 if (SCF.LOG_LEVEL <= SCF.DEBUG) { 277 window.console.debug.apply(window.console, arguments); 278 } 279 }, 280 info: function() { 281 if (SCF.LOG_LEVEL <= SCF.INFO) { 282 window.console.info.apply(window.console, arguments); 283 } 284 }, 285 warn: function() { 286 if (SCF.LOG_LEVEL <= SCF.WARN) { 287 window.console.warn.apply(window.console, arguments); 288 } 289 }, 290 error: function() { 291 if (SCF.LOG_LEVEL <= SCF.ERROR) { 292 window.console.error.apply(window.console, arguments); 293 } 294 } 295 }; 296 var deepCommentSearch = function(node, regex) { 297 var child = node.firstChild; 298 var foundNode = null; 299 while (child) { 300 switch (child.nodeType) { 301 case 1: 302 foundNode = deepCommentSearch(child, regex); 303 break; 304 case 8: 305 if (child.nodeValue.match(regex)) { 306 return child; 307 } 308 break; 309 } 310 if (foundNode !== null) { 311 break; 312 } 313 child = child.nextSibling; 314 } 315 return foundNode; 316 }; 317 SCF.Router = new Backbone.Router(); 318 if (!Backbone.History.started) { 319 Backbone.history.start({ 320 pushState: true, 321 hashChange: false 322 }); 323 } 324 SCF.View = Backbone.View.extend({ 325 initialize: function() { 326 this._rendered = false; 327 this._childViews = {}; 328 this._parentView = undefined; 329 this._modelReady = false; 330 this._sessionReady = false; 331 this._renderedChildren = []; 332 this._replacementTarget = null; 333 this._destroyed = false; 334 if (this.$el.html() !== "") { 335 this.bindView(); 336 this._rendered = true; 337 } 338 this.listenTo(this.model, "model:loaded", function() { 339 this._modelReady = true; 340 this.render(); 341 }); 342 this.listenTo(this.model, "model:cacheFixed", function() { 343 this.render(); 344 }); 345 if (this.requiresSession && !SCF.Session.isReady()) { 346 //SCF.log.debug("%s waiting for session to be ready.", this.cid); 347 this.listenTo(SCF.Session, "model:loaded", function(data) { 348 if (!this._sessionReady) { 349 //SCF.log.debug("View: %s got Session ready.", this.cid); 350 this._sessionReady = true; 351 this.render(); 352 } 353 }); 354 } 355 this._sessionReady = SCF.Session.isReady(); 356 if (_.isFunction(this.init)) { 357 this.init.apply(this, arguments); 358 } 359 if (SCF.Session.isReady()) { 360 this.initContext(); 361 } else { 362 SCF.Session.on("model:loaded", _.bind(this.initContext, this)); 363 } 364 }, 365 initContext: function() { 366 if (_.isUndefined(SCF.Context)) { 367 SCF.Context = {}; 368 var groupNavbarSel = ".scf-js-group-navbar"; 369 370 // get site path to be written into SCF.Context and recorded for analytics 371 var sitePath = $(".scf-js-site-title").attr("href"); 372 sitePath = _.isUndefined(sitePath) ? "" : sitePath.substring(0, sitePath.lastIndexOf(".html")); 373 this.sitePath = sitePath; 374 /* 375 * Populate page level component information into SCF.Context 376 * to be sent with analytics calls. It is particularly important 377 * for events such as post (SCFCreate) or votes (SCFVote) originated 378 * in nested coments or replies (not top level) that do not have 379 * direct access to this information 380 */ 381 SCF.Context.siteTitle = $(".scf-js-site-title").length !== 0 ? $(".scf-js-site-title").text() : ""; 382 SCF.Context.sitePath = this.sitePath; 383 SCF.Context.groupTitle = $(groupNavbarSel).length !== 0 && !_.isUndefined($(groupNavbarSel).attr("data-group-title")) ? $(groupNavbarSel).data("group-title") : ""; 384 SCF.Context.groupPath = $(groupNavbarSel).length !== 0 && !_.isUndefined($(groupNavbarSel).attr("data-group-path")) ? $(groupNavbarSel).data("group-
384path") : ""; 385 SCF.Context.user = SCF.Session.get("authorizableId"); 386 } 387 if (_.isFunction(this.initAnalytics)) { 388 this.initAnalytics.apply(this, arguments); 389 } 390 }, 391 getContextForTemplate: function() { 392 var context = (this.model !== undefined) ? this.model.toJSON() : this.context; 393 return this.getMergedContexts(context); 394 }, 395 getMergedContexts: function(context) { 396 if (!_.isObject(context)) { 397 context = {}; 398 } 399 context.loggedInUser = SCF.Session.toJSON(); 400 context.environment = {}; 401 context.environment.client = true; 402 return context; 403 }, 404 appendTo: function(parentElement) { 405 if (!this._rendered) { 406 this.render(); 407 } 408 $CQ(parentElement).append(this.el); 409 this.trigger("view:ready", { 410 view: this 411 }); 412 }, 413 replaceElement: function(replacedElement) { 414 if (!this._rendered) { 415 this.render(); 416 } 417 if (this._rendered) { 418 $CQ(replacedElement).replaceWith(this.$el); 419 this._replacementTarget = null; 420 this.trigger("view:ready", { 421 view: this 422 }); 423 } else { 424 //SCF.log.debug("Attaching replacementTarget: %s", this.cid); 425 this._replacementTarget = replacedElement; 426 } 427 }, 428 render: function() { 429 if (this._destroyed) { 430 return; 431 } 432 var that = this; 433 if (!(this._modelReady || this.model._isReady) || (this.requiresSession && !this._sessionReady)) { 434 /* 435 if (!(this._modelReady || this.model._isReady)) { 436 SCF.log.debug("Skipping render due to Model not ready %s : %s", this.cid, this.model.attributes.resourceType); 437 } 438 if (this.requiresSession && !this._sessionReady) { 439 SCF.log.debug("Skipping render due to Session not ready %s : %s", this.cid, this.model.attributes.resourceType); 440 } 441 */ 442 return this; 443 } 444 //SCF.log.debug("Rendering %s : %s", this.cid, this.model.attributes.resourceType); 445 this.unbindDataFields(); 446 for (var viewName in this._childViews) { 447 this._childViews[viewName].destroy(); 448 delete this._childViews[viewName]; 449 } 450 this._renderedChildren = []; 451 var element = $CQ(this.template(this.getContextForTemplate(), { 452 data: { 453 parentView: this 454 } 455 })); 456 //Check if its attached to DOM or rendered 457 if (this._rendered || this.$el.parent().length > 0) { 458 this.$el.html(element.html()); 459 } else { 460 this.setElement(element); 461 } 462 //render children 463 _.each(this._childViews, function(child) { 464 that.renderChildView(child); 465 }); 466 467 var finishRendering = _.bind(function() { 468 this.bindView(); 469 this._rendered = true; 470 if (this.afterRender) { 471 this.afterRender(); 472 } 473 this.trigger("view:rendered", { 474 view: this 475 }); 476 477 }, this); 478 //wait for children to finish rendering and then complete binding the view 479 $CQ.when(this._renderedChildren).done(finishRendering); 480 if (this._replacementTarget !== null) { 481 this.replaceElement(this._replacementTarget); 482 } 483 return this; 484 }, 485 bindView: function() { 486 var that = this; 487 this.unbindDataFields(); 488 this.$("[evt]").not(this.$("[data-scf-component] [evt]")).each(function(idx, trigger) { 489 SCF.View.bindEvents(that, $CQ(trigger)); 490 }); 491 this.$("[data-attrib]").not(this.$("[data-scf-component] [data-attrib]")).each(function(idx, element) { 492 SCF.View.bindDataFields(that, $CQ(element)); 493 }); 494 this.$("[data-form]").not(this.$("[data-scf-component] [data-form]")).each(function(idx, element) { 495 SCF.View.bindDataForms(that, $CQ(element)); 496 }); 497 }, 498 addChildView: function(childView) {
499 //SCF.log.debug("Adding Child View: %s", childView.cid); 500 this._childViews[childView.cid] = childView; 501 var deferred = $CQ.Deferred(); 502 this._renderedChildren[childView.cid] = deferred.promise(); 503 this.listenTo(childView, "view:rendered", function() { 504 deferred.resolve(); 505 }); 506 this.listenTo(childView, "view:destroyed", function(event) { 507 //SCF.log.debug("Parent getting destory command for child view: %s", event.view.cid); 508 this.removeChildView(event.view.cid); 509 }); 510 return this; 511 }, 512 getChildView: function(childViewID) { 513 return this._childViews[childViewID]; 514 }, 515 removeChildView: function(childViewID) { 516 if (this._renderedChildren.hasOwnProperty(childViewID)) { 517 this._renderedChildren[childViewID].fail(); 518 } 519 if (this._childViews.hasOwnProperty(childViewID)) { 520 var childView = this._childViews[childViewID]; 521 childView.stopListening(); 522 this.stopListening(childView, "view:rendered"); 523 this._childViews[childViewID] = undefined; 524 delete this._childViews[childViewID]; 525 } 526 return this; 527 }, 528 getChildViews: function() { 529 return this._childViews; 530 }, 531 setParent: function(parentView) { 532 this._parentView = parentView; 533 parentView.addChildView(this); 534 return this; 535 }, 536 renderChildView: function(view) { 537 //SCF.log.debug("Rendering child view: %s", view.cid); 538 view.render(); 539 var parent = this; 540 if (parent.el === null) { 541 return; 542 } 543 var el = null; 544 var currentNode = null; 545 var targetView = new RegExp("\s*?data-view='" + view.cid + "'"); 546 if (document.createNodeIterator && NodeFilter && NodeFilter.SHOW_COMMENT) { 547 var iter = document.createNodeIterator(parent.el, NodeFilter.SHOW_COMMENT, 548 function(node) { 549 if (node.data.match(targetView)) { 550 return NodeFilter.FILTER_ACCEPT; 551 } 552 }, 553 false 554 ); 555 currentNode = iter.nextNode(); 556 while (currentNode !== null) { 557 el = currentNode; 558 currentNode = iter.nextNode(); 559 break; 560 } 561 view.replaceElement(el); 562 } else { 563 el = deepCommentSearch(parent.el, targetView); 564 view.replaceElement(el); 565 } 566 }, 567 getField: function(field) { 568 var element = this._fields[field]; 569 if (element) { 570 return element.val(); 571 } 572 return ""; 573 }, 574 setField: function(field, data) { 575 var element = this._fields[field]; 576 if (!element) { 577 return; 578 } 579 element.val(data); 580 }, 581 focus: function(field) { 582 var element = this._fields[field]; 583 if (!element) { 584 return; 585 } 586 element.focus(); 587 }, 588 getForm: function(form) { 589 if (typeof this._forms === 'undefined') { 590 return null; 591 } else { 592 return this._forms[form]; 593 } 594 }, 595 destroy: function() { 596 this.undelegateEvents(); 597 this.unbindDataFields(); 598 this.stopListening(); 599 this.trigger("view:destroyed", { 600 view: this 601 }); 602 this._destroyed = true; 603 //SCF.log.debug("DESTORYING %s : %s", this.cid, this.model.attributes.resourceType); 604 }, 605 unbindDataFields: function() { 606 for (var prop in this._fields) { 607 if (this._fields.hasOwnProperty(prop)) { 608 if (_.isFunction(this._fields[prop].destroy)) { 609 this._fields[prop].destroy(); 610 } 611 } 612 } 613 this._fields = {}; 614 }, 615 log: _logger 616 }); 617 SCF.View.extend = function() { 618 var child = Backbone.View.extend.apply(this, arguments); 619 var viewName = arguments[0].viewName; 620 SCF.Views[viewName] = child; 621 return child; 622 }; 623 624 SCF.Model = Backbone.Model.extend({ 625 _cachedModels: {}, 626 _hasLoadedChildren: false, 627 parse: function(response) { 628 this._parseRelations(response); 629 return response; 630 }, 631 addEncoding: function(data) { 632 if ((window.FormData) && (data instanceof window.FormData)) { 633 data.append("_charset_", "UTF-8"); 634 } 635 if (!data.hasOwnProperty("_charset_")) { 636 data._charset_ = "UTF-8"; 637 } 638 return data; 639 }, 640 reload: function(callback) { 641 this._isReady = false;
642 var url = ""; 643 var urlFn; 644 if (_.isFunction(this.url)) { 645 //Need to do this since model.clear will clear the id that is used to construct the url 646 url = this.url(); 647 urlFn = this.url; 648 } else { 649 //seen code that sets the param as a hardcoded string 650 url = this.url; 651 } 652 this.clear(); 653 //This.clear clears the id resulting in bad URL so setting the URL for fetch to happen 654 if (!_.isEmpty(url)) { 655 this.url = url; 656 } 657 var that = this; 658 this.fetch({ 659 dataType: "json", 660 cache: false, 661 xhrFields: { 662 withCredentials: true 663 }, 664 error: function(model, response) { 665 SCF.log.error("Error fetching model"); 666 SCF.log.error(response); 667 model.clear(); 668 model._isReady = true; 669 model.trigger("model:loaded", model); 670 if (callback && typeof(callback.error) === "function") { 671 callback.error(); 672 } 673 }, 674 success: function(model) { 675 if (urlFn !== undefined) { 676 //resetting the url back to function 677 model.url = urlFn; 678 } 679 model._isReady = true; 680 model.trigger("model:loaded", model); 681 if (callback && typeof(callback.success) === "function") { 682 callback.success(); 683 } 684 } 685 }); 686 }, 687 reset: function(attributes, options) { 688 this.clear().set(_.clone(this.defaults)); 689 var attr = this._parseRelations(attributes); 690 this.set(attr, options); 691 return this; 692 }, 693 initialize: function(attributes) { 694 this.listenTo(SCF.Session, "logout:success", function() { 695 this.reload(); 696 }); 697 this.listenTo(SCF.Session, "login:success", function() { 698 this.reload(); 699 }); 700 }, 701 constructor: function(attributes, options) { 702 if(typeof(attributes) !== "object") { 703 attributes = JSON.parse(attributes); 704 } 705 var attr = this._parseRelations(attributes); 706 Backbone.Model.apply(this, [attr, options]); 707 }, 708 url: function() { 709 var u; 710 if (this.urlRoot) { 711 u = this.urlRoot + this.id + SCF.constants.URL_EXT; 712 } else if (SCF.config.urlRoot) { 713 u = SCF.config.urlRoot + this.id + SCF.constants.URL_EXT; 714 } else { 715 u = this.id + SCF.constants.URL_EXT; 716 } 717 return u; 718 }, 719 _parseRelations: function(attributes) { 720 if(typeof(attributes) !== "object") { 721 attributes = JSON.parse(attributes); 722 } 723 var makeRelation = _.bind(function(data, key) { 724 if (!attributes[key] && !data.path) { 725 attributes[key] = []; 726 } 727 if (attributes[key] || data.path) { 728 var relative = attributes[key]; 729 var ModelKlass, model; 730 if (_.isArray(relative)) { 731 var modelArray = [], 732 idArray = []; 733 _.each(relative, function(rel) { 734 if (_.isObject(rel)) { 735 ModelKlass = !_.isUndefined(SCF.Models[data.model]) ? SCF.Models[data.model] : SCF.Components[rel.resourceType].Model; 736 model = ModelKlass.findLocal(rel.id); 737 if (!model) { 738 model = ModelKlass.createLocally(rel); 739 } else { 740 model.reset(rel); 741 } 742 modelArray.push(model); 743 } else if (!_.isEmpty(rel)) { 744 var idFromUrl = rel.substr(SCF.config.urlRoot.length); 745 idFromUrl = idFromUrl.substr(0, idFromUrl.lastIndexOf(SCF.constants.URL_EXT)); 746 ModelKlass = SCF.Models[data.model]; 747 model = ModelKlass.findLocal("idFromUrl"); 748 if (!model) { 749 model = data.autofetch ? ModelKlass.find(idFromUrl) : new ModelKlass({ 750 url: rel 751 }); 752 } 753 ModelKlass.prototype._cachedModels[idFromUrl] = model; 754 modelArray.push(model); 755 } 756 }); 757 var CollectionKlass = SCF.Collections[data.collection] || Backbone.Collection; 758 var collection = new CollectionKlass(); 759 collection.model = ModelKlass; 760 collection.parent = this; 761 collection.set(modelArray, { 762 silent: true 763 }); 764 attributes[key] = collection; 765 } else if (_.isObject(relative)) { 766 if (_.isUndefined(SCF.Models[data.model]) && _.isUndefined(SCF.Components[relative.resourceType])) { 767 this.log.error("A relation key %s requested model %s but it is not available nor is the component type: %s", key, data.model, relative.resourceType); 768 return; 769 } 770 ModelKlass = SCF.Models[data.model] || SCF.Components[relative.resourceType].Model; 771 model = ModelKlass.findLocal(relative.id) || ModelKlass.createLocally(relative); 772 attributes[key] = model; 773 } else { 774 var url = relative; 775 if (!url) { 776 if (data.path) { 777 if (data.path.substr(0, 1) === "/") { 778 url = data.path; 779 } else { 780 url = SCF.config.urlRoot + attributes.id + "/" + data.path + SCF.constants.URL_EXT; 781 } 782 } else { 783 return; 784 } 785 } 786 ModelKlass = SCF.Models[data.model]; 787 if (data.autofetch) { 788 model = ModelKlass.find(url, undefined, true); 789 } else { 790 model = ModelKlass.findLocal(url, true) || new ModelKlass({ 791 "url": url 792 }); 793 } 794 attributes[key] = model; 795 } 796 } 797 }, this); 798 _.each(this.relationships, makeRelation); 799 return attributes; 800 }, 801 toJSON: function() { 802 var json = Backbone.Model.prototype.toJSON.apply(this); 803 _.each(this.relationships, function(config, relation) { 804 805 var relative = json[relation]; 806 if (relative.length <= 0) { 807 delete json[relation]; 808 return; 809 } 810 if (_.isArray(relative)) { 811 var jsonArray = []; 812 _.each(relative, function(rel) { 813 if (rel instanceof Backbone.Model) 814 jsonArray.push(rel.toJSON()); 815 else 816 jsonArray.push(rel); 817 }); 818 json[relation] = jsonArray; 819 } else if (relative instanceof Backbone.Collection) { 820 json[relation] = relative.toJSON(); 821 } else if (relative instanceof Backbone.Model) { 822 json[relation] = relative.toJSON(); 823 } 824 825 }); 826 return json; 827 }, 828 log: _logger 829 }); 830 SCF.Model.extend = function() { 831 var child = Backbone.Model.extend.apply(this, arguments); 832 var modelName = arguments[0].modelName; 833 SCF.Models[modelName] = child; 834 return child; 835 }; 836 SCF.View.bindEvents = function(view, eventTrigger) { 837 var eventString = eventTrigger.attr("evt"); 838 _.each(eventString.split(","), function(value) { 839 var parts = value.split("="); 840 var evt = $CQ.trim(parts[0]); 841 var func = $CQ.trim(parts[1]); 842 if (view[func]) { 843 var eventHandler = _.bind(view[func], view); 844 eventTrigger.off(evt); 845 eventTrigger.on(evt, eventHandler); 846 } 847 }); 848 }; 849 SCF.View.bindDataFields = function(view, element) { 850 var field = element.attr("data-attrib"); 851 if (!view._fields) { 852 view._fields = {}; 853 } 854 if (!_.isUndefined(view._fields[field])) { 855 return; 856 } 857 var fieldType = element.attr("data-field-type"); 858 var ManagerKlass = (_.isUndefined(SCF.fieldManagers[fieldType])) ? DefaultFieldType : SCF.fieldManagers[fieldType]; 859 var manager = new ManagerKlass(element, {}, view.model); 860 view._fields[field] = (function() { 861 return { 862 val: function() { 863 if (arguments.length === 0) 864 return manager.getValue(); 865 else 866 return manager.setValue(arguments[0]); 867 }, 868 focus: function() { 869 return manager.focus(); 870 }, 871 destroy: function() { 872 if (_.isFunction(manager.destroy)) { 873 manager.destroy(); 874 } 875 } 876 }; 877 })(); 878 }; 879 SCF.View.bindDataForms = function(view, element) { 880 var form = element.attr("data-form"); 881 if (!view._forms) { 882 view._forms = {}; 883 } 884 view._forms[form] = new SCFValidator($(element), false); 885 }; 886 SCF.Model.findLocal = function(mid, isUrl) { 887 var id = isUrl ? mid.substr(SCF.config.urlRoot.length) : mid; 888 if (this.prototype._cachedModels && this.prototype._cachedModels[id]) { 889 return this.prototype._cachedModels[id]; 890 } 891 }; 892 SCF.Model.createLocally = function(attributes) { 893 var modelObj = new this.prototype.constructor(attributes); 894 modelObj._isReady = true; 895 this.prototype._cachedModels[modelObj.get("id")] = modelObj; 896 return modelObj; 897 }; 898 SCF.Model.prototype.load = function(mid) { 899 if (mid) { 900 this.set({ 901 "id": mid 902 }, { 903 silent: true 904 }); 905 } 906 this.fetch({ 907 success: function(model) { 908 model._isReady = true; 909 model.trigger("model:loaded", model); 910 }, 911 xhrFields: { 912 withCredentials: true 913 } 914 }); 915 }; 916 SCF.Model.prototype.getConfigValue = function(key) { 917 var config = this.get("configuration"); 918 if (!_.isEmpty(config)) { 919 return config[key]; 920 } 921 return null; 922 }; 923 SCF.Model.prototype.destroy = function(options) { 924 var model = this; 925 this.constructor.prototype._cachedModels[model.get("id")] = undefined; 926 model.trigger("destroy", model, model.collection, options); 927 }; 928 929 SCF.Model.prototype.parseServerError = function(jqxhr, text, error) { 930 var errorDetails = $CQ.parseJSON(jqxhr.responseText); 931 if (errorDetails.hasOwnProperty("status.code")) { 932 errorDetails.status = errorDetails.status || {}; 933 errorDetails.status.code = errorDetails["status.code"]; 934 delete errorDetails["status.code"]; 935 } 936 if (errorDetails.hasOwnProperty("status.message")) { 937 errorDetails.status = errorDetails.status || {}; 938 errorDetails.status.message = errorDetails["status.message"]; 939 delete errorDetails["status.message"]; 940 } 941 return { 942 "error": error, 943 "details": errorDetails 944 }; 945 }; 946 947 SCF.Model.find = function(mid, callback, isUrl) { 948 var that = this; 949 if (this.prototype._cachedModels && this.prototype._cachedModels[mid]) { 950 var model = this.prototype._cachedModels[mid]; 951 if (_.isFunction(callback)) { 952 callback(model); 953 } 954 return model; 955 } else { 956 var newModel = new this.prototype.constructor({ 957 id: mid 958 }); 959 if (isUrl) { 960 newModel.url = mid; 961 } 962 //TODO figure out caching mechanism 963 this.prototype._cachedModels[mid] = newModel; 964 newModel.fetch({ 965 dataType: "json", 966 xhrFields: { 967 withCredentials: true 968 }, 969 error: function(model, response) { 970 if (response.status === 204 || response.status === 404) { 971 SCF.log.debug("non existing resource"); 972 model._isReady = true; 973 model.trigger("model:loaded", model); 974 if (_.isFunction(callback)) { 975 callback(model); 976 } 977 } else { 978 SCF.log.error("Error fetching model"); 979 SCF.log.error(response); 980 that.prototype._cachedModels[mid] = undefined; 981 } 982 983 }, 984 success: function(model) { 985 model._isReady = true; 986 model.trigger("model:loaded", model); 987 if (_.isFunction(callback)) { 988 callback(model); 989 } 990 } 991 }); 992 return newModel; 993 } 994 }; 995 SCF.Collection = Backbone.Collection.extend({}); 996 SCF.Collection.extend = function() { 997 var child = Backbone.Collection.extend.apply(this, arguments); 998 var collectioName = arguments[0].collectioName; 999 SCF.Collections[collectioName] = child; 1000 return child; 1001 }; 1002 1003 SCF.registerComponent = function(componentName, modelKlass, viewKlass) { 1004 SCF.Components[componentName] = { 1005 Model: modelKlass, 1006 View: viewKlass, 1007 name: componentName 1008 }; 1009 }; 1010 1011 SCF.addLoadedComponent = function(resourceType, model, view) { 1012 if (!SCF.Components[resourceType]) { 1013 return; 1014 } 1015 if (!SCF.loadedComponents[resourceType]) { 1016 SCF.loadedComponents[resourceType] = {}; 1017 } 1018 SCF.loadedComponents[resourceType][model.id] = { 1019 "model": model, 1020 "view": view 1021 }; 1022 return SCF.loadedComponents[resourceType][model.id]; 1023 }; 1024 SCF.findTemplate = function(resourceId, templateName, resourceType) { 1025 if (arguments.length == 2) { 1026 resourceType = templateName; 1027 templateName = ""; 1028 }
1029 var templateKey = resourceType + "/" + templateName; 1030 if (SCF.templates[templateKey]) { 1031 return SCF.templates[templateKey]; 1032 } 1033 var template; 1034 $CQ.ajax({ 1035 async: false, 1036 // xhrFields: { 1037 // withCredentials: true 1038 // }, 1039 url: SCF.config.urlRoot + "/services/social/templates" + "?resourceType=" + resourceType + "&ext=hbs&selector=" + templateName 1040 }).done(function(data, status) { 1041 if (status == "success") { 1042 template = Handlebars.compile(data); 1043 SCF.templates[templateKey] = template; 1044 } 1045 }); 1046 return template; 1047 }; 1048 1049 SCF.log = _logger; 1050 1051 SCF.registerFieldType = function(fieldType, fieldTypeManager) { 1052 if (!(_.isFunction(fieldTypeManager.prototype.setValue))) { 1053 this.log.error("%s does not implement required method, \"setValue\"", fieldType); 1054 return; 1055 } 1056 if (!(_.isFunction(fieldTypeManager.prototype.getValue))) { 1057 this.log.error("%s does not implement required method, \"getValue\"", fieldType); 1058 return; 1059 } 1060 if (!(_.isFunction(fieldTypeManager.prototype.focus))) { 1061 this.log.error("%s does not implement required method, \"focus\"", fieldType); 1062 return; 1063 } 1064 if (!(_.isFunction(fieldTypeManager.prototype.destroy))) { 1065 this.log.error("%s does not implement required method, \"destroy\"", fieldType); 1066 return; 1067 } 1068 this.fieldManagers[fieldType] = fieldTypeManager; 1069 }; 1070 1071 var DefaultFieldType = function(element, config, model) { 1072 this.$el = element; 1073 }; 1074 1075 DefaultFieldType.prototype.setValue = function(val) { 1076 return this.$el.val(val); 1077 }; 1078 DefaultFieldType.prototype.getValue = function() { 1079 return this.$el.val(); 1080 }; 1081 DefaultFieldType.prototype.focus = function() { 1082 this.$el.focus(); 1083 }; 1084 DefaultFieldType.prototype.destroy = function() {}; 1085 1086 SCF.View.prototype.launchModal = function(element, header, closeCallBack) { 1087 var modalScreen = $CQ("<div class=\"scf scf-modal-screen\"></div>"); 1088 var modalDialog = $CQ("<div class=\"scf scf-modal-dialog\" style=\"display:none;\">" + 1089 "<h2 class=\"scf-modal-header\">" + header + 1090 "</h2><div class=\"scf-modal-close\" tabindex=\"0\" role=\"button\" aria-pressed=\"false\">X</div></div>"); 1091 var el = element; 1092 var parent = el.parent(); 1093 modalDialog.append(el); 1094 el.show(); 1095 var close = function(e) { 1096 if (SCF.Util.mayCall(e, "preventDefault")) { 1097 e.preventDefault(); 1098 } 1099 el.hide(); 1100 parent.append(el); 1101 modalScreen.remove(); 1102 modalDialog.remove(); 1103 if (_.isFunction(closeCallBack)) { 1104 closeCallBack(); 1105 } 1106 }; 1107 1108 var closeByEnter = function(e) { 1109 if(e.which == 13) { 1110 if (SCF.Util.mayCall(e, "preventDefault")) { 1111 e.preventDefault(); 1112 } 1113 el.hide(); 1114 parent.append(el); 1115 modalScreen.remove(); 1116 modalDialog.remove(); 1117 if (_.isFunction(closeCallBack)) { 1118 closeCallBack(); 1119 } 1120 } 1121 }; 1122 1123 modalDialog.find(".scf-modal-close").keypress(closeByEnter); 1124 modalDialog.find(".scf-modal-close").click(close); 1125 modalDialog.find(".scf-js-modal-close").click(close); 1126 1127 $CQ("body").append(modalScreen); 1128 $CQ("body").append(modalDialog); 1129 var width = (window.innerWidth - modalDialog.innerWidth()) / 2; 1130 var height = (window.innerHeight - modalDialog.innerHeight()) / 2; 1131 modalDialog.css({ 1132 "top": height, 1133 "left": width 1134 }); 1135 modalDialog.show(); 1136 SCF.Util.addTabbingInDialog(modalDialog) ; 1137 return close; 1138 }; 1139 1140 SCF.View.prototype.overlayTemplate = "<div class=\"scf-overlay\">" + 1141 "<div class=\"scf-overlay-header btn-toolbar\">" + 1142 "<button class=\"btn btn-primary scf-ovelay-back-button\" title=\"{{i18n \"Back\"}}\">" + 1143 "<span class=\"scf-icon-left\"></span>" + 1144 "</button>" + 1145 "<h3>{{header}}</h3>" + 1146 "</div>" + 1147 "</div>"; 1148 SCF.View.prototype.loadOverlay = function(element, parent, header, closeCallback) { 1149 var template = Handlebars.compile(this.overlayTemplate);
1150 var overlay = $CQ(template({ 1151 'header': header 1152 })); 1153 var close = function() { 1154 overlay.remove(); 1155 parent.find(".scf-is-overlay-hidden").each(function() { 1156 $CQ(this).removeClass("scf-is-overlay-hidden"); 1157 }); 1158 if (closeCallback && _.isFunction(closeCallBack)) { 1159 closeCallBack(); 1160 } 1161 }; 1162 parent.children().each(function() { 1163 $CQ(this).addClass("scf-is-overlay-hidden"); 1164 }); 1165 overlay.append(element); 1166 parent.append(overlay); 1167 overlay.find(".scf-ovelay-back-button").click(close); 1168 return close; 1169 }; 1170 SCF.View.prototype.errorTemplate = "<h3>{{details.status.message}}</h3>"; 1171 SCF.View.prototype.addErrorMessage = function(element, error) { 1172 var template = Handlebars.compile(this.errorTemplate); 1173 var $el = $CQ(element); 1174 var $errorElement = $CQ(template(error)); 1175 $errorElement.addClass("scf-js-error-message"); 1176 $el.before($errorElement); 1177 }; 1178 SCF.View.prototype.compileTemplate = function(hbsMarkup) { 1179 return Handlebars.compile(hbsMarkup); 1180 }; 1181 SCF.View.prototype.clearErrorMessages = function(element, error) { 1182 this.$el.find(".scf-js-error-message").remove(); 1183 this.$el.find(".scf-error").removeClass("scf-error"); 1184 }; 1185 1186 SCF.ChildView = SCF.View.extend({ 1187 bindView: function() {}, 1188 bindDataForms: function() {}, 1189 bindDataFields: function() {}, 1190 bindEvents: function() {}, 1191 viewName: "ChildView" 1192 }); 1193 1194 SCF.Util = { 1195 // Allows you to pass in an object and see if the funcName is avaiable ot be called, 1196 // this only does a shallow check for now. 1197 "mayCall": function(obj, funcName) { 1198 if (_.isUndefined(obj) || _.isNull(obj)) { 1199 return false; 1200 } 1201 return (obj.hasOwnProperty(funcName) || obj[funcName] !== null) && _.isFunction(obj[funcName]); 1202 }, 1203 "announce": function(channel, data) { 1204 $CQ(document).trigger(channel, data); 1205 }, 1206 "listenTo": function(channel, listener) { 1207 $CQ(document).on(channel, function(e, data) { 1208 listener(data); 1209 e.stopImmediatePropagation(); 1210 }); 1211 }, 1212 "startsWith": function(sourceString, searchString) { 1213 return sourceString.substr(0, searchString.length) === searchString; 1214 }, 1215 "getContextPath": function() { 1216 var URL = CQ.shared.HTTP.getPath(); 1217 var pageExtension = CQ.shared.HTTP.getExtension(); 1218 var urlSplit = URL.split(pageExtension); 1219 if (urlSplit && urlSplit !== undefined) { 1220 if (urlSplit.length > 1) { 1221 return urlSplit[1]; 1222 } else { 1223 return urlSplit[0]; 1224 } 1225 } 1226 return ""; 1227 }, 1228 "addTabbingInDialog": function(e) { 1229 var inputs = e.find('select, input, textarea, button, a,div').filter(':visible'); 1230 var firstInput = inputs.first(); 1231 var lastInput = inputs.last(); 1232 firstInput.focus(); 1233 lastInput.on('keydown', function(e) { 1234 if ((e.which === 9 && !e.shiftKey)) { 1235 e.preventDefault(); 1236 firstInput.focus(); 1237 } 1238 }); 1239 /*redirect first shift+tab to last input*/ 1240 firstInput.on('keydown', function(e) { 1241 if ((e.which === 9 && e.shiftKey)) { 1242 e.preventDefault(); 1243 lastInput.focus(); 1244 } 1245 }); 1246 return close; 1247 } 1248 }; 1249 window.SCF = SCF; 1250 1251})(Backbone, $CQ, _, Handlebars); 1252 1253/* 1254 * 1255 * ADOBE CONFIDENTIAL 1256 * __________________ 1257 * 1258 * Copyright 2012 Adobe Systems Incorporated 1259 * All Rights Reserved. 1260 *
1261 * NOTICE: All information contained herein is, and remains 1262 * the property of Adobe Systems Incorporated and its suppliers, 1263 * if any. The intellectual and technical concepts contained 1264 * herein are proprietary to Adobe Systems Incorporated and its 1265 * suppliers and are protected by trade secret or copyright law. 1266 * Dissemination of this information or reproduction of this material 1267 * is strictly forbidden unless prior written permission is obtained 1268 * from Adobe Systems Incorporated. 1269 */ 1270(function(Handlebars, moment, SCF, $CQ, _, CQ) { 1271 "use strict"; 1272 1273 var slingInclude = function(path, templateName, resourceType) { 1274 var html = ""; 1275 var params = { 1276 resourcePath: path 1277 }; 1278 if (resourceType) { 1279 SCF.log.warn("Forcing resource type is not supported when sling including on the client side"); 1280 } 1281 if (templateName) { 1282 params.selector = templateName; 1283 } 1284 var urlToFetch = SCF.config.urlRoot + path; 1285 urlToFetch += templateName ? "." + templateName + ".html" : ".html"; 1286 $CQ.ajax({ 1287 async: false, 1288 // xhrFields: { 1289 // withCredentials: true 1290 // }, 1291 url: urlToFetch 1292 }).done(function(data, status) { 1293 if (status == "success") { 1294 html = data; 1295 } 1296 }); 1297 return new Handlebars.SafeString(html); 1298 }; 1299 Handlebars.registerHelper("include", function(context, options) { 1300 1301 1302 if (arguments.length === 1) { 1303 options = context; 1304 context = undefined; 1305 } 1306 var parentView = options.data.parentView; 1307 var getModelName = function(viewName) { 1308 if (!viewName) { 1309 return undefined; 1310 } 1311 var idx = viewName.lastIndexOf("View"); 1312 if (idx !== -1) { 1313 return viewName.substr(0, idx) + "Model"; 1314 } else { 1315 return viewName + "Model"; 1316 } 1317 }; 1318 var bindModelView = _.isUndefined(options.hash.bind) ? true : options.hash.bind; 1319 var viewName = options.hash.view; 1320 var templateName = options.hash.template; 1321 var resourceType = options.hash.resourceType; 1322 var path = options.hash.path; 1323 var modelName = options.hash.model || getModelName(viewName); 1324 var viewObj, modelObj, ViewKlass, ModelKlass, id, component; 1325 1326 1327 if (_.isObject(context)) { 1328 resourceType = resourceType || context.resourceType; 1329 component = SCF.Components[resourceType]; 1330 if ((_.isUndefined(component)) && (resourceType.match(/^(\/apps)|(\/libs)/))) { 1331 var baseType = resourceType.substring(6); 1332 component = SCF.Components[baseType]; 1333 } 1334 var cTemplate; 1335 1336 id = context.id; 1337 if (!id) { 1338 var url = context.url; 1339 if (!url) { 1340 SCF.log.warn("No resource id found for context: "); 1341 SCF.log.warn(context); 1342 } 1343 var idFromUrl = url.substr(SCF.config.urlRoot.length); 1344 idFromUrl = idFromUrl.substr(0, idFromUrl.lastIndexOf(SCF.constants.URL_EXT)); 1345 id = idFromUrl; 1346 } 1347 1348 if (templateName) { 1349 cTemplate = SCF.findTemplate(id, templateName, resourceType); 1350 } else { 1351 cTemplate = SCF.findTemplate(id, resourceType); 1352 } 1353 1354 var getViewKlass = function() { 1355 //use an SCF.ChildView if the template being included belongs to the same component and rendering the same resource 1356 if (parentView.model.get("resourceType") === resourceType && parentView.model.id === id) { 1357 return SCF.ChildView; 1358 } 1359 return component ? component.View : undefined; 1360 }; 1361 1362 ViewKlass = viewName ? SCF.Views[viewName] : getViewKlass(); 1363 ViewKlass = bindModelView ? ViewKlass : undefined; 1364 ModelKlass = modelName ? SCF.Models[modelName] : component ? component.Model : undefined; 1365 ModelKlass = bindModelView ? ModelKlass : undefined; 1366 1367 if (!ViewKlass && !cTemplate) { 1368 if (id) { 1369 return slingInclude(id, templateName, resourceType); 1370 } 1371 SCF.log.error("No view or template found for " + resourceType + " and template " + templateName); 1372 return ""; 1373 } 1374 1375 1376 if (!ViewKlass && cTemplate) { 1377 return new Handlebars.SafeString(cTemplate(SCF.View.prototype.getMergedContexts(context))); 1378 } 1379 1380 1381 if (ViewKlass && !cTemplate) { 1382 SCF.log.error("No template found for " + resourceType + " and template " + templateName); 1383 return ""; 1384 } 1385 1386 if (!ModelKlass || !id) { 1387 viewObj = new ViewKlass({ 1388 "context": context 1389 }); 1390 } else { 1391 modelObj = ModelKlass.findLocal(id); 1392 if (!modelObj) { 1393 modelObj = ModelKlass.createLocally(context); 1394 } 1395 if (modelObj.isNew()) { 1396 modelObj.load(id); 1397 } 1398 viewObj = new ViewKlass({ 1399 model: modelObj 1400 }); 1401 1402 } 1403 if (templateName && cTemplate) { 1404 viewObj.template = cTemplate; 1405 } else if (cTemplate) { 1406 ViewKlass.prototype.template = cTemplate; 1407 } 1408 1409 } else { 1410 1411 var isPathAbsolute = path ? path.slice(0, 1) === "/" : false;
1412 if (!context && !isPathAbsolute) { 1413 SCF.log.error("Must provide context path when including " + resourceType); 1414 return ""; 1415 } 1416 1417 id = isPathAbsolute ? path : context + "/" + path; 1418 1419 if (resourceType) { 1420 component = SCF.Components[resourceType]; 1421 } 1422 if (bindModelView && (component || (viewName && modelName))) { 1423 ViewKlass = !component ? SCF.Views[viewName] : component.View; 1424 ModelKlass = !component ? SCF.Models[modelName] : component.Model; 1425 } 1426 1427 if (ViewKlass && ModelKlass) { 1428 var isUrl = id.indexOf("http://") === 0; 1429 modelObj = ModelKlass.find(id, undefined, isUrl); 1430 viewObj = new ViewKlass({ 1431 "model": modelObj 1432 }); 1433 if (templateName) { 1434 viewObj.template = SCF.findTemplate(id, templateName, resourceType); 1435 } else if (typeof viewObj.template === "undefined") { 1436 SCF.log.info("Getting default template for " + resourceType); 1437 viewObj.template = SCF.findTemplate(id, resourceType, resourceType); 1438 } 1439 } else { 1440 return slingInclude(id, templateName, resourceType); 1441 } 1442 } 1443 viewObj.setParent(parentView); 1444 if (!ViewKlass.prototype.template && viewObj.template && ViewKlass != SCF.ChildView) { 1445 ViewKlass.prototype.template = SCF.findTemplate(modelObj.get("id"), resourceType); 1446 1447 } 1448 viewObj.templateName = templateName || "default"; 1449 viewObj.resource = id; 1450 return new Handlebars.SafeString("<!-- data-view='" + viewObj.cid + "'-->"); 1451 }); 1452 1453 Handlebars.registerHelper("equals", function(lvalue, rvalue, options) { 1454 if (arguments.length < 3) 1455 throw new Error("Handlebars Helper equal needs 2 parameters"); 1456 if (lvalue != rvalue) { 1457 return options.inverse(this); 1458 } else { 1459 return options.fn(this); 1460 } 1461 }); 1462 1463 Handlebars.registerHelper('lt', function(a, b) { 1464 var next = arguments[arguments.length - 1]; 1465 return (a < b) ? next.fn(this) : next.inverse(this); 1466 }); 1467 1468 Handlebars.registerHelper("lastPath", function(context, options) { 1469 var idx = context.lastIndexOf("/"); 1470 return context.slice(idx + 1); 1471 }); 1472 1473 Handlebars.registerHelper("getComponentName", function(context, options) { 1474 return context.split('/')[1]; 1475 }); 1476 1477 Handlebars.registerHelper("pretty-time", function(context, options) { 1478 if (!context) { 1479 return ""; 1480 } 1481 var time = new Date(context); 1482 var now = new Date(); 1483 var diff = now.getTime() - time.getTime(); 1484 var second = 1000; 1485 var minute = second * 60; 1486 var hour = minute * 60; 1487 var day = hour * 24; 1488 moment.locale(CQ.shared.I18n.getLocale()); 1489 // max days ago before switching to actual date. If not passed in then hardcoding as 60. 1490 var days_cutoff = options.hash.daysCutoff ? options.hash.daysCutoff : 60; 1491 if (diff < minute) { 1492 time = Math.round(diff / second) + ""; 1493 if (time == 1) { 1494 return new Handlebars.SafeString(CQ.I18n.get("{0} second ago", time)); 1495 } 1496 return new Handlebars.SafeString(CQ.I18n.get("{0} seconds ago", time)); 1497 } else if (diff < hour) { 1498 time = Math.round(diff / minute); 1499 if (time == 1) { 1500 return new Handlebars.SafeString(CQ.I18n.get("{0} minute ago", time)); 1501 } 1502 return new Handlebars.SafeString(CQ.I18n.get("{0} minutes ago", time)); 1503 } else if (diff < day) { 1504 time = Math.round(diff / hour); 1505 if (time == 1) { 1506 return new Handlebars.SafeString(CQ.I18n.get("{0} hour ago", time)); 1507 } 1508 return new Handlebars.SafeString(CQ.I18n.get("{0} hours ago", time)); 1509 } else if (diff < day * days_cutoff) { 1510 time = Math.round(diff / day); 1511 if (time == 1) {
1512 return new Handlebars.SafeString(CQ.I18n.get("{0} day ago", time)); 1513 } 1514 return new Handlebars.SafeString(CQ.I18n.get("{0} days ago", time)); 1515 } else { 1516 return new Handlebars.SafeString(moment(time).format(CQ.I18n.get("MMM DD YYYY, h:mm A", null, "moment.js, communities moderation"))); 1517 } 1518 }); 1519 1520 Handlebars.registerHelper("pages", function(context, options) { 1521 var pageInfo = context; 1522 1523 if (pageInfo.hasOwnProperty("selectedPage") && pageInfo.hasOwnProperty("totalPages") && pageInfo.hasOwnProperty("pageSize") && pageInfo.hasOwnProperty("basePageURL")) { 1524 var output = ""; 1525 if (pageInfo.totalPages <= 1) { 1526 return output; 1527 } 1528 var pageSize = Math.abs(pageInfo.pageSize); 1529 var pageSign = (pageInfo.orderReversed) ? "-" : ""; 1530 var currentPage = pageInfo.selectedPage; 1531 1532 var leftLimit = currentPage; 1533 if ((leftLimit - 2) > 0 && pageInfo.totalPages > 5) { 1534 leftLimit = leftLimit - 2; 1535 } else if (leftLimit == 2 && pageInfo.totalPages > 5) { 1536 //for https://jira.corp.adobe.com/browse/NPR-30827 1537 leftLimit = 1; 1538 } 1539 1540 if (pageInfo.totalPages <= 5) { 1541 leftLimit = 1; 1542 } else { 1543 if (pageInfo.totalPages - currentPage < 2) { 1544 leftLimit = pageInfo.totalPages - 4; 1545 } 1546 } 1547 var rightLimit = leftLimit + 5; 1548 if (rightLimit > pageInfo.totalPages) { 1549 rightLimit = pageInfo.totalPages + 1; 1550 } 1551 1552 for (var i = leftLimit; i < rightLimit; i++) { 1553 pageInfo.pageNumber = i; 1554 pageInfo.currentPageUrl = pageInfo.basePageURL + "." + ((i - 1) * pageSize) + "." + pageSign + pageSize + ".html"; 1555 pageInfo.currentPage = i == pageInfo.selectedPage; 1556 pageInfo.suffix = ((i - 1) * pageSize) + "." + pageSign + pageSize; 1557 output += options.fn(pageInfo); 1558 } 1559 return output; 1560 } else { 1561 return ""; 1562 } 1563 }); 1564 1565 Handlebars.registerHelper("loadmore", function(context, options) { 1566 var pageInfo = context.pageInfo; 1567 var items = context.items; 1568 if (!context.totalSize || !pageInfo) { 1569 return ""; 1570 } 1571 if (!(!_.isUndefined(pageInfo.selectedPage) && context.totalSize && pageInfo.pageSize)) { 1572 return ""; 1573 } 1574 if (context.totalSize <= 0) { 1575 return ""; 1576 } 1577 var info = {}; 1578 info.suffix = pageInfo.nextSuffix; 1579 var remaining = this.totalSize; 1580 if (!_.isUndefined(items)) { 1581 remaining = remaining - items.length; 1582 } 1583 if (remaining === 0) { 1584 return ""; 1585 } 1586 var url = pageInfo.nextPageURL; 1587 if (!_.isUndefined(url) && url.indexOf(".json", url.length - 5) !== -1) { 1588 url = url.substr(0, url.length - 5); 1589 url += ".html"; 1590 } 1591 info.remaining = remaining; 1592 info.moreURL = url; 1593 return options.fn(info); 1594 }); 1595 1596 Handlebars.registerHelper("dateUtil", function(context, options) { 1597 var date = context; 1598 var format = options.hash.format; 1599 var timezone = options.hash.timezone; 1600 if (!date || typeof date != "number") { 1601 date = new Date().getTime(); 1602 } else { 1603 date = new Date(date); 1604 } 1605 format = format.replace(/y/g, "Y"); // replace java "yyyy" with moment "YYYY" 1606 format = format.replace(/\bdd\b/gi, "DD"); // replace java "dd" with moment "DD" 1607 format = format.replace(/\bd\b/gi, "D"); // replace java "d" with moment "D" 1608 format = format.replace(/\bEEEE\b/gi, "dddd"); 1609 moment.locale(CQ.shared.I18n.getLocale()); 1610 1611 if (timezone && moment.tz) { 1612 return new Handlebars.SafeString(moment.tz(date, timezone).format(format)); 1613 } 1614 1615 return new Handlebars.SafeString(moment(date).format(format)); 1616 }); 1617 1618 Handlebars.registerHelper("i18n", function(context, options) { 1619 if (arguments.length > 1) { 1620 var i18nArgs = _.rest(arguments); 1621 return CQ.I18n.get(context, i18nArgs); 1622 } else { 1623 return CQ.I18n.get(context); 1624 } 1625 }); 1626 1627 Handlebars.registerHelper("xss-htmlAttr", function(context, options) { 1628 //encodeForHTMLAttr 1629 var $div = $CQ("div"); 1630 $div.attr("data-xss", context); 1631 var cleaned = $div.attr("data-xss"); 1632 return CQ.shared.XSS.getXSSValue(cleaned); 1633 // if (!context) { 1634 // return ""; 1635 // } 1636 // return new Handlebars.SafeString(context.toString().replace(/\./g, '-')); 1637 }); 1638 Handlebars.registerHelper("xss-jsString", function(context, options) { 1639 //encodeForJSString 1640 return CQ.shared.XSS.getXSSValue(context); 1641 }); 1642 Handlebars.registerHelper("xss-html", function(context, options) { 1643 //encodeForHTML 1644 return $CQ("<div></div>").text(context).html(); 1645 }); 1646 Handlebars.registerHelper("xss-validHref", function(context, options) { 1647 //getValidHref 1648 return encodeURI(context); 1649 }); 1650 Handlebars.registerHelper("dom-id", function(context, options) { 1651 if (!context) { 1652 return ""; 1653 } 1654 var domId = $CQ.trim(context); 1655 domId = domId.replace(/\./g, "-"); 1656 domId = domId.replace(/\//g, "-"); 1657 domId = domId.replace(/:/g, "-"); 1658 return domId; 1659 }); 1660 Handlebars.registerHelper("abbreviate", function(context, options) { 1661 1662 if (!context) { 1663 return ""; 1664 } 1665 var maxWords = options.hash.maxWords; 1666 var maxLength = options.hash.maxLength; 1667 var safeString = options.hash.safeString; 1668 var ctx = $CQ.trim(context); 1669 var initialLength = ctx.length; 1670 1671 var words = ctx.substring(0, maxLength).split(" "); 1672 var abb = words.slice(0, words.length > maxWords ? maxWords : words.length).join(" "); 1673 var abbContent = initialLength != abb.length && options.fn ? options.fn(this) : ""; 1674 if (safeString) { 1675 return new Handlebars.SafeString(abb) + abbContent; 1676 } 1677 return abb + abbContent; 1678 }); 1679 1680 Handlebars.registerHelper("includeClientLib", function(context, options) { 1681 // This helper only works on the server side. 1682 return ""; 1683 }); 1684 1685 Handlebars.registerHelper("if-wcm-mode", function(context, options) { 1686 // This helper only works on the server side. 1687 return ""; 1688 }); 1689 1690 Handlebars.registerHelper("getContextPath", function(context, options) { 1691 var contextPath = ""; 1692 if (Granite && Granite.HTTP.getContextPath()) { 1693 contextPath = Granite.HTTP.getContextPath(); 1694 } 1695 return contextPath; 1696 }); 1697 1698})(Handlebars, moment, SCF, $CQ, _, CQ); 1699 1700/* 1701 * 1702 * ADOBE CONFIDENTIAL 1703 * __________________ 1704 * 1705 * Copyright 2012 Adobe Systems Incorporated 1706 * All Rights Reserved. 1707 *
1708 * NOTICE: All information contained herein is, and remains 1709 * the property of Adobe Systems Incorporated and its suppliers, 1710 * if any. The intellectual and technical concepts contained 1711 * herein are proprietary to Adobe Systems Incorporated and its 1712 * suppliers and are protected by trade secret or copyright law. 1713 * Dissemination of this information or reproduction of this material 1714 * is strictly forbidden unless prior written permission is obtained 1715 * from Adobe Systems Incorporated. 1716 */ 1717(function($CQ, SCF, _, CQ, Granite) { 1718 "use strict"; 1719 var LoginView = SCF.View.extend({ 1720 viewName: "Login", 1721 tagName: "div", 1722 className: "scf-login", 1723 init: function() { 1724 this._isReady = false; 1725 this.listenTo(this.model, this.model.events.LOGGED_IN_SUCCESS, this.render); 1726 this.listenTo(this.model, this.model.events.LOGGED_OUT, this.render); 1727 }, 1728 loginAction: function() { 1729 if (this.model.get("loggedIn")) { 1730 this.$el.children(".login-dialog").hide(); 1731 this.logout(); 1732 } else { 1733 var loginDialog = this.$el.children(".login-dialog").toggle(); 1734 loginDialog.find("input:first").focus(); 1735 } 1736 }, 1737 logout: function() { 1738 this.model.logout(); 1739 }, 1740 login: function() { 1741 var username = this.getField("username"); 1742 var password = this.getField("password"); 1743 if (username === "" || password === "") { 1744 return; 1745 } 1746 this.model.login(username, password); 1747 } 1748 }); 1749 var LoginModel = SCF.Model.extend({ 1750 moderatorCheckAttribute: "moderatorCheck", 1751 events: { 1752 LOGGED_IN_SUCCESS: "login:success", 1753 LOGGED_IN_FAIL: "login:failed", 1754 LOGGED_OUT: "logout:success" 1755 }, 1756 initialize: function(attributes, options) { 1757 this._isReady = false; 1758 if (CQ.shared.User.data === undefined || CQ.shared.User.data === null) { 1759 //Dont call the currentuser.json if data is available 1760 this.initUser(options); 1761 } else { 1762 this.getLoggedInUser(options); 1763 } 1764 }, 1765 defaults: { 1766 "loggedIn": false 1767 }, 1768 isReady: function() { 1769 return this._isReady; 1770 }, 1771 checkIfModeratorFor: function(resource) { 1772 var componentList = this.attributes.hasOwnProperty(this.moderatorCheckAttribute) ? 1773 this.attributes[this.moderatorCheckAttribute] : []; 1774 return this.attributes.loggedIn && _.contains(componentList, resource); 1775 }, 1776 checkIfUserCanPost: function(resource) { 1777 var componentList = this.attributes.hasOwnProperty("mayPost") ? 1778 this.attributes.mayPost : []; 1779 return this.attributes.loggedIn && _.contains(componentList, resource); 1780 }, 1781 setLanguage: function(data) { 1782 var langFromPreferences = data.preferences && 1783 data.preferences.language ? 1784 data.preferences.language : 1785 "en"; 1786 var language = document.documentElement.lang || langFromPreferences; 1787 CQ.shared.I18n.setLocale(language); 1788 }, 1789 initUser: function(options) { 1790 var CURRENT_USER_URL = CQ.shared.HTTP.externalize("/libs/granite/security/currentuser" + CQ.shared.HTTP.EXTENSION_JSON + "?props=preferences/language"); 1791 CURRENT_USER_URL = CQ.shared.HTTP.noCaching(CURRENT_USER_URL); 1792 var that = this; 1793 $CQ.ajax({ 1794 url: CURRENT_USER_URL, 1795 type: "GET", 1796 success: function(result) { 1797 that.getLoggedInUser(options, result.home); 1798 that.setLanguage(result); 1799 }, 1800 async: false 1801 }); 1802 }, 1803 getLoggedInUser: function(options, userPath) { 1804 var that = this; 1805 var moderationCheckParameter; 1806 if (options.hasOwnProperty(LoginModel.moderatorCheckAttribute)) { 1807 moderationCheckParameter = "?" + LoginModel.moderatorCheckAttribute + "="; 1808 _.each(options[LoginModel.moderatorCheckAttribute], function(item) { 1809 moderationCheckParameter += item + ","; 1810 }); 1811 moderationCheckParameter = moderationCheckParameter.substring(0, moderationCheckParameter.length - 1); 1812 } 1813 var userHomePath = ""; 1814 if (userPath !== undefined) { 1815 userHomePath = userPath; 1816 } else if (CQ.shared.User.initialized) { 1817 userHomePath = CQ.shared.User.data.home; 1818 } else { 1819 // AEM user not initialized force it: 1820 var f = CQ.shared.User.init(); 1821 userHomePath = CQ.shared.User.data.home; 1822 } 1823 1824 $CQ.ajax({ 1825 url: SCF.config.urlRoot + "/services/social/getLoggedInUser" + moderationCheckParameter + "&h=" + userHomePath, 1826 xhrFields: { 1827 withCredentials: true 1828 }, 1829 type: "GET" 1830 }).done(function(user) { 1831 if (user.name) { 1832 that.set({ 1833 "loggedIn": true 1834 }); 1835 that.set(user); 1836 } 1837 that._isReady = true; 1838 if (typeof options !== "undefined" && options.silent) { 1839 that.trigger("model:loaded", { 1840 model: that, 1841 silent: true 1842 }); 1843 } else { 1844 that.trigger("model:loaded", { 1845 model: that, 1846 silent: false 1847 }); 1848 } 1849 }); 1850 }, 1851 logout: function() { 1852 var that = this; 1853 $CQ.ajax({ 1854 url: SCF.config.urlRoot + "/services/social/logout", 1855 xhrFields: { 1856 withCredentials: true 1857 }, 1858 type: "GET" 1859 }).always(function() { 1860 that.clear(); 1861 that.trigger(that.events.LOGGED_OUT); 1862 }); 1863 }, 1864 login: function(username, password) { 1865 var that = this; 1866 $CQ.ajax({ 1867 url: SCF.config.urlRoot + "/libs/login.html/j_security_check", 1868 xhrFields: { 1869 withCredentials: true 1870 }, 1871 data: { 1872 j_username: username, 1873 j_password: password, 1874 j_validate: "true" 1875 }, 1876 type: "POST" 1877 }).success(function(loginResult, textStatus, jqXHR, id) { 1878 var amIAuthenticated = jqXHR.getResponseHeader("Set-Cookie")
1878=== null || jqXHR.getResponseHeader("Set-Cookie") !== ""; 1879 if (!amIAuthenticated) { 1880 this.trigger(this.events.LOGGED_IN_FAIL, { 1881 "user": username 1882 }); 1883 } else { 1884 that.getLoggedInUser(); 1885 that.trigger(that.events.LOGGED_IN_SUCCESS, { 1886 "user": username 1887 }); 1888 } 1889 }); 1890 } 1891 }); 1892 LoginModel.moderatorCheckAttribute = "moderatorCheck"; 1893 SCF.LoginView = LoginView; 1894 SCF.LoginModel = LoginModel; 1895 1896 SCF.registerComponent("login", SCF.LoginModel, SCF.LoginView); 1897 1898})($CQ, SCF, _, CQ, Granite); 1899 1900/* 1901 * 1902 * ADOBE CONFIDENTIAL 1903 * __________________ 1904 * 1905 * Copyright 2012 Adobe Systems Incorporated 1906 * All Rights Reserved. 1907 * 1908 * NOTICE: All information contained herein is, and remains 1909 * the property of Adobe Systems Incorporated and its suppliers, 1910 * if any. The intellectual and technical concepts contained 1911 * herein are proprietary to Adobe Systems Incorporated and its 1912 * suppliers and are protected by trade secret or copyright law. 1913 * Dissemination of this information or reproduction of this material 1914 * is strictly forbidden unless prior written permission is obtained 1915 * from Adobe Systems Incorporated. 1916 */ 1917//file: bootstrap.js 1918(function(_, $CQ, Backbone, Handlebars, SCF) { 1919 "use strict"; 1920 var contextPath = CQ.shared.HTTP.getContextPath(); 1921 SCF.events = SCF.events || {}; 1922 SCF.events.BOOTSTRAP_REQUEST = "scf-bootstrap-request"; 1923 1924 SCF.config.urlRoot = window.location.protocol + "//" + window.location.host; 1925 if (contextPath !== null && contextPath.length > 0) { 1926 SCF.config.urlRoot += contextPath; 1927 } 1928 var addView = function(component) { 1929 var model = component.model; 1930 //If the component type isn't registered do nothing 1931 if (SCF.Components[component.type]) { 1932 var templateUsed = component.template ? 1933 SCF.findTemplate(component.id, component.template, component.type) : 1934 SCF.findTemplate(component.id, component.type); 1935 var view = new SCF.Components[component.type].View({ 1936 "model": model, 1937 el: component.el 1938 }); 1939 if (component.template) { 1940 view.template = templateUsed; 1941 } else { 1942 SCF.Components[component.type].View.prototype.template = templateUsed; 1943 } 1944 view.templateName = component.template || "default"; 1945 view.resource = component.id; 1946 // Bootstrap was not determining parent views at all which broke updates 1947 _.each(SCF.loadedComponents, function(typeObject) { 1948 _.each(typeObject, function(type, id) { 1949 if (model.attributes.hasOwnProperty("parentId")) { 1950 if (id === model.attributes.parentId) { 1951 view.setParent(type.view); 1952 } 1953 } else if (view._parentView === undefined || view._parentView === null) { 1954 // If there isn't a parent ID in the data wire it up via the dom 1955 // Search for a parent to this el that is an SCF component, meaning it has 1956 // both and ID and a resource type. 1957 var $parentEl = view.$el.parents("[data-component-id][data-scf-component]"); 1958 if ($parentEl && $parentEl.length === 1) { 1959 var domParentId = $parentEl.attr("data-component-id"); 1960 var resourceType = $parentEl.attr("data-scf-component"); 1961 var parentSCFComponentByResource = SCF.loadedComponents[resourceType]; 1962 // Check to make sure a component is registered for this type. 1963 if (parentSCFComponentByResource !== undefined) { 1964 var parentSCFComponent = parentSCFComponentByResource[domParentId]; 1965 // Make sure the id is registered and it really does have a view. 1966 if (parentSCFComponent !== undefined && parentSCFComponent.hasOwnProperty("view")) { 1967 view.setParent(parentSCFComponent.view); 1968 } 1969 } 1970 } 1971 } 1972 }); 1973 }); 1974 if (model.cacheFixed) { 1975 view.render(); 1976 } 1977 component.view = view; 1978 } 1979 }; 1980 1981 var addModel = function(component) {
1982 //If the component type isn't registered do nothing 1983 if (SCF.Components[component.type]) { 1984 var model; 1985 var modelHolder = component.modelHolder; 1986 var ModelKlass = SCF.Components[component.type].Model; 1987 if (modelHolder.length > 0) { 1988 var modelText = $CQ(modelHolder[0]).text(); 1989 if (modelText === "") { 1990 modelText = modelHolder[0].text; 1991 } 1992 var modelJSON = $CQ.parseJSON(modelText); 1993 component.id = modelJSON.id; 1994 model = ModelKlass.findLocal(component.id); 1995 if (!model) { 1996 model = SCF.Components[component.type].Model.createLocally(modelJSON); 1997 } 1998 } else { 1999 model = ModelKlass.findLocal(component.id); 2000 if (!model) { 2001 // if we didn't find the model load it based on the ID which is the path to the component. 2002 model = SCF.Components[component.type].Model.find(component.id); 2003 } 2004 } 2005 component.model = model; 2006 } 2007 }; 2008 2009 // Creates a component based on the scf ID the resource type and an optional template. 2010 var createComponent = function(id, type, template, $el) { 2011 // Find the json model on the page. 2012 var modelHolder = $CQ("script[type='application/json'][id='" + id + "']"); 2013 var component = { 2014 id: id, 2015 type: type, 2016 template: template, 2017 modelHolder: modelHolder, 2018 el: $el 2019 }; 2020 var model = addModel(component); 2021 var view = addView(component); 2022 return SCF.addLoadedComponent(type, model, view); 2023 }; 2024 // A helper method for inspecting a scf component piece of markup and extract some data from it. 2025 var extractComponentFromElement = function($el) { 2026 var component = { 2027 id: $el.attr("data-component-id"), 2028 type: $el.data("scf-component"), 2029 template: $el.data("scf-template"), 2030 modelHolder: $CQ("script[type='application/json'][id='" + $el.attr("data-component-id") + "']"), 2031 el: $el 2032 }; 2033 2034 return component; 2035 }; 2036 2037 var fullBootstrap = function() { 2038 var $CQcomponents = $CQ("[data-scf-component]"); 2039 var allComponents = [], 2040 componentsToBoostrap = []; 2041 // for each component on the page get the meta data. 2042 $CQcomponents.each(function(idx, el) { 2043 var component = extractComponentFromElement($(el)); 2044 if (!SCF.loadedComponents[component.type] || !SCF.loadedComponents[component.type][component.id]) { 2045 componentsToBoostrap.push(component); 2046 } 2047 allComponents.push(component); 2048 }); 2049 2050 // If there were components startup the user model. This gives the user model a bit of head start and reduces flicker on the screen. 2051 // Allow newly added components to reinvoke the Session model for all the components to ensure 2052 // The session has alll the component's moderator attributes set correctly. (even thought they 2053 // live at a page level.) 2054 if (componentsToBoostrap.length > 0) { 2055 var options = {}; 2056 options.silent = true; 2057 options[SCF.LoginModel.moderatorCheckAttribute] = _.map(allComponents, function(item) { 2058 var dataObj; 2059 if (item.id.indexOf("/content/usergenerated") === -1) { 2060 // If the component itself isn't in usergenerated then we should check it for moderators 2061 return item.id; 2062 } 2063 try { 2064 dataObj = JSON.parse(item.modelHolder.text()); 2065 } catch (e) { 2066 // If the component's data can't be turned in JSON just skip it, something 2067 // is probably wrong if this is happening. 2068 return false; 2069 } 2070 if (!(dataObj.hasOwnProperty("sourceComponentId"))) { 2071 // If the component doesn't have a sourceComponentId then it's not moderatable, 2072 // any tallies for example. 2073 return false;
2074 } 2075 if (dataObj.sourceComponentId.indexOf("/content/usergenerated") !== -1) { 2076 // If the source component is also in user generated then we shouldn't need to check it as the 2077 // content parent is where configuration for moderation lives. 2078 return false; 2079 } 2080 return dataObj.sourceComponentId; 2081 2082 }); 2083 options[SCF.LoginModel.moderatorCheckAttribute] = _.compact(options[SCF.LoginModel.moderatorCheckAttribute]); 2084 if (SCF.Session) { 2085 SCF.Session.getLoggedInUser(options, undefined); 2086 } else { 2087 var log = new SCF.LoginModel({}, options); 2088 SCF.Session = log; 2089 } 2090 } 2091 2092 2093 _.each(componentsToBoostrap, function(component) { 2094 addModel(component); 2095 }); 2096 _.each(componentsToBoostrap, function(component) { 2097 addView(component); 2098 SCF.addLoadedComponent(component.type, component.model, component.view); 2099 }); 2100 }; 2101 2102 $CQ(document).ready(fullBootstrap); 2103 //Sometimes this script could be loaded multiple times 2104 if (!Backbone.History.started) { 2105 Backbone.history.start({ 2106 pushState: true, 2107 hasChange: false 2108 }); 2109 } 2110 $(document).on(SCF.events.BOOTSTRAP_REQUEST, fullBootstrap); 2111 SCF.addComponent = function(el) { 2112 var $el = $(el); 2113 if ($el.length === 0) { 2114 throw "Could not find requested element on page."; 2115 } 2116 var component = extractComponentFromElement($el); 2117 if (component === null) { 2118 throw "Component is already loaded."; 2119 } 2120 if (!component.id) { 2121 throw "Component does not have a data-component-id attribute, which is required"; 2122 } 2123 if (!component.type) { 2124 throw "Component does not have a data-scf-component attribute, which is required."; 2125 } 2126 return createComponent(component.id, component.type, component.template, component.el); 2127 }; 2128 2129 SCF.unloadComponent = function(id, type) { 2130 var typeList = SCF.loadedComponents[type]; 2131 if (typeList === null) { 2132 throw "Type " + type + " is not registered with SCF."; 2133 } 2134 var component = SCF.loadedComponents[type][id]; 2135 if (component === null || component === undefined) { 2136 throw "Could not find component with ID: " + id; 2137 } 2138 component.view.destroy(); 2139 component.model = null; 2140 delete SCF.loadedComponents[type][id]; 2141 }; 2142})(_, $CQ, Backbone, Handlebars, SCF); 2143 2144/* 2145 * 2146 * ADOBE CONFIDENTIAL 2147 * __________________ 2148 * 2149 * Copyright 2012 Adobe Systems Incorporated 2150 * All Rights Reserved. 2151 *
2152 * NOTICE: All information contained herein is, and remains 2153 * the property of Adobe Systems Incorporated and its suppliers, 2154 * if any. The intellectual and technical concepts contained 2155 * herein are proprietary to Adobe Systems Incorporated and its 2156 * suppliers and are protected by trade secret or copyright law. 2157 * Dissemination of this information or reproduction of this material 2158 * is strictly forbidden unless prior written permission is obtained 2159 * from Adobe Systems Incorporated. 2160 */ 2161(function(Backbone, $CQ, _, Handlebars) { 2162 "use strict"; 2163 2164 var CKRte = function(element, config, model, view) { 2165 var rteConfig = {}; 2166 rteConfig = _.extend(config, rteConfig); 2167 rteConfig = _.extend(this.config, rteConfig); 2168 var el = element.get()[0]; 2169 if (_.isUndefined(window.CKEDITOR)) { 2170 SCF.log.error("Rich text editor requested but unable to find CKEDITOR please include client library category: \"%s\" or disable RTE", "ckeditor"); 2171 return; 2172 } 2173 this.$el = element; 2174 var height = this.$el.data("editorheight"); 2175 var uploadUrl = SCF.config.urlRoot + model.get("id") + SCF.constants.URL_EXT; 2176 var modelConfigAttachmentAllowed = model.get("configuration"); 2177 modelConfigAttachmentAllowed = modelConfigAttachmentAllowed && modelConfigAttachmentAllowed.isAttachmentAllowed; 2178 2179 2180 if (rteConfig.extraPlugins === undefined) { 2181 rteConfig.extraPlugins = (window.CKEDITOR.config.extraPlugins) ? window.CKEDITOR.config.extraPlugins + 2182 "," : undefined; 2183 } 2184 2185 // Mentions support 2186 // We have embedded the mentions plugin in ckeditor - so no need to add it as an extra plugin (will be beneficial for performance) 2187 // Just check here if mentions is enabled for the component and then make the necessary configuration for the mentions support 2188 if (model.attributes && model.attributes.configuration && model.attributes.configuration.enableMentions) { 2189 2190 rteConfig.extraAllowedContent = 'a[*]'; // This is to enable hyperlinks to mentioned user's profiles in ckeditor 2191 2192 // This defines how you will see the users in the mentions drop down list 2193 var mentionsDropDownTemplate = '<li data-id="{id}"><img src="{avatarUrl}" alt="{name}">{name}</li>'; 2194 2195 // This defines how the mentioned user will look once we select them while writing the post . 2196 // Howeer when viewing the published post/comment mentions user will be dsiplayed as defined by UIMentionsPattern design property for that component 2197 var mentionsOutputTemplate = '<a href = "{friendlyUrl}" class = "social-mention" data-social-mention-authorizableid = "{authorizableId}">{name}</a>'; 2198 2199 var minCharsToTriggerMentionsFeed = 4; //should we make this configurable via a design property ? OR would it be an overkill ? 2200 2201 var maxLimitForMentionsDropDown = 20; //same thought ? should we make this configurable via a design property ? OR would it be an overkill ? 2202 2203 rteConfig.mentions = [{feed: function(options, callback) { 2204 var searchFor = options.query; 2205 var userListNodePath = model.attributes.sourceComponentId ? model.attributes.sourceComponentId : model.attributes.id; 2206 var dataUrl = window.location.origin + userListNodePath + '/userlist.social.0.20.json' + 2207 '?type=users&filter=[{"operation":"CONTAINS","./@rep:principalName":"' + 2208 searchFor + '"},{"operation":"CONTAINS","./@rep:principalName":"' + 2209 searchFor + "*" + '"},{"operation":"CONTAINS","profile/@givenName":"' + 2210 searchFor + "*" + '"},{"operation":"CONTAINS","profile/@familyName":"' + 2211 searchFor + "*" + '"}]&fromPublisher=true&_charset_=utf-8'; 2212 $.ajax({ 2213 async: false, 2214 url: Granite.HTTP.externalize(dataUrl), 2215 type: 'GET', 2216 success: function(resp) { 2217 if(resp.items != undefined) { 2218 callback(JSON.parse(JSON.stringify(resp.items))); 2219 } 2220 } 2221 }); 2222 },minChars: minCharsToTriggerMentionsFeed,itemsLimit: maxLimitForMentionsDropDown,itemTemplate: mentionsDropDownTemplate, outputTemplate: mentionsOutputTemplate}]; 2223 } 2224 2225 var toolbarArr = rteConfig.toolbar[0].items; 2226 var index; 2227 // Add oEmbed plugin by default for Blogs Articles and Calendar Events 2228 if (model.get("resourceType") === "social/journal/components/hbs/journal" || 2229 model.get("resourceType") === "social/calendar/components/hbs/calendar" || 2230 model.get("resourceType") === "social/ideation/components/hbs/ideation" || 2231 model.get("resourceType") === "social/ideation/components/hbs/idea" || 2232 (model.get("resourceType") === "social/calendar/components/hbs/event" || 2233 model.get("resourceType") === "social/journal/components/hbs/entry_topic") && 2234 element[0].dataset.rteType !== 'comment') { 2235 2236 // Add the oembed Icon to toolbar 2237 index = toolbarArr.indexOf("oembed"); 2238 if (index === -1) { 2239 rteConfig.toolbar[0].items.push("oembed"); 2240 } 2241 2242 if (rteConfig.extraPlugins === undefined) { 2243 rteConfig.extraPlugins = "oembed"; 2244 } else if (rteConfig.extraPlugins.length > 0 && rteConfig.extraPlugins.indexOf("oembed") === -1) { 2245 rteConfig.extraPlugins = rteConfig.extraPlugins.concat(",oembed"); 2246 } 2247 } else { // Need to remove the oembed plugin for the Blog Comment 2248 index = toolbarArr.indexOf("oembed"); 2249 if (index > 0) { 2250 toolbarArr.splice(index, 1); 2251 } 2252 } 2253 2254 if (modelConfigAttachmentAllowed) { 2255 rteConfig.filebrowserUploadUrl = uploadUrl; 2256 rteConfig.uploadUrl = uploadUrl; 2257 2258 // Add the Image Icon to toolbar 2259 index = toolbarArr.indexOf("Image"); 2260 if (index === -1) { 2261 rteConfig.toolbar[0].items.push("Image"); 2262 } 2263 2264 if (rteConfig.extraPlugins === undefined) { 2265 rteConfig.extraPlugins = "image2,uploadimage"; 2266 } else if (rteConfig.extraPlugins.length > 0 && rteConfig.extraPlugins.indexOf("image2,uploadimage") === -1) { 2267 rteConfig.extraPlugins = rteConfig.extraPlugins.concat(",image2,uploadimage"); 2268 } 2269 } else { 2270 // Add the Image Icon to toolbar 2271 index = toolbarArr.indexOf("Image"); 2272 if (index === -1) { 2273 rteConfig.toolbar[0].items.push("Image"); 2274 } 2275 2276 if (rteConfig.extraPlugins === undefined) { 2277 rteConfig.extraPlugins = "image2"; 2278 } else if (rteConfig.extraPlugins.length > 0 && rteConfig.extraPlugins.indexOf("image2") === -1) { 2279 rteConfig.extraPlugins = rteConfig.extraPlugins.concat(",image2"); 2280 } 2281 } 2282 2283 var domElementName = $CQ(el).attr("name"); 2284 if (_.isEmpty(domElementName)) { 2285 var modelId = model.get("id"); 2286 var idx = modelId.lastIndexOf("/"); 2287 modelId = modelId.slice(idx + 1); 2288 var attribName = $CQ(el).data("attrib"); 2289 modelId = attribName + "-" + modelId; 2290 $CQ(el).attr("name", modelId); 2291 domElementName = modelId; 2292 } 2293 var resizeEnabled = this.$el.data("editorresize"); 2294 if (resizeEnabled) { 2295 rteConfig.resize_enabled = true; 2296 } 2297 2298 if (_.isNumber(height)) { 2299 rteConfig.height = height; 2300 } 2301 2302 if (!window.CKEDITOR.instances[domElementName]) { 2303 this.editor = window.CKEDITOR.replace(el, rteConfig); 2304 } else { 2305 if (this.editor === undefined) { 2306 this.editor = window.CKEDITOR.instances[domElementName]; 2307 } 2308 } 2309 /*if (_.isNumber(height)) { 2310 delete this.config.height; 2311 } 2312 if (resizeEnabled) { 2313 delete this.config.resize_enabled; 2314 }*/ 2315 this.model = model; 2316 if (modelConfigAttachmentAllowed) { 2317 this.editor.on("fileUploadRequest", this.attachFileFromDragAndDrop); 2318 this.editor.on("fileUploadResponse", this.handleFileUploadResponse); 2319 this.changeImagePluginDialog(); 2320 } 2321 }; 2322 2323 CKRte.prototype.destroy = function() { 2324 if (this.editor) { 2325 try { 2326 if (this.editor.filter && this.editor.window && this.editor.window.getFrame()) { 2327 this.editor.destroy(true); 2328 this.editor.removeAllListeners(); 2329 } else { 2330 this.editor.removeAllListeners(); 2331 window.CKEDITOR.remove(this.editor); 2332 window.CKEDITOR.fire("instanceDestroyed", null, this.editor); 2333 } 2334 } catch (e) { 2335 SCF.log.error("Couldn't destroy editor: %o", e); 2336 } 2337 } 2338 delete this.editor; 2339 return; 2340 }; 2341 2342 2343 CKRte.prototype.getFileIFrameFromDialog = function(definition) { 2344 var dialogDefinition = definition; 2345 var contents = dialogDefinition.contents; 2346 for (var i = 0; i < contents.length; i++) { 2347 var contentObject = contents[i]; 2348 var contentObjectId = contentObject.id; 2349 // Code specific to image plugin. They have give the ID as Upload 2350 if (contentObjectId == "Upload") { 2351 var elements = contentObject.elements; 2352 for (var j = 0; j < elements.length; j++) { 2353 var element = elements[j];
2354 var elementId = element.id; 2355 if (elementId == "uploadButton") { 2356 // set the custom Function 2357 element.onClick = this.setCustomFileButtonClick; 2358 element["for"] = ["Upload", "file"]; 2359 } 2360 if (elementId == "upload") { 2361 element.id = "file"; 2362 } 2363 } 2364 } 2365 } 2366 }; 2367 2368 CKRte.prototype.setCustomFileButtonClick = function(evt) { 2369 var target = evt.sender["for"]; 2370 var dialog = evt.data.dialog; 2371 var fileElement = dialog.getContentElement(target[0], target[1]); 2372 var fileIframe = $CQ("#" + fileElement.domId + " iframe"); 2373 //set additional parameters for the upload to happen 2374 var fileIframeForm = fileIframe.contents().find("form"); 2375 var success = _.bind(function(response) { 2376 var responseObj; 2377 if(typeof(response.response) !== "object") { 2378 responseObj = JSON.parse(response.response); 2379 } else { 2380 responseObj = response.response; 2381 } 2382 var location = responseObj.url; 2383 location = CQ.shared.HTTP.encodePath(location); 2384 dialog.getContentElement("info", "src").setValue(location); 2385 dialog.selectPage("info"); 2386 if ($CQ(".cke_dialog_ui_input_text").length !== 0) { 2387 $CQ(".cke_dialog_ui_input_text").focus(); 2388 } 2389 }, this); 2390 var error = _.bind(function(response) { 2391 SCF.log.error("Failed to upload file" + response); 2392 alert("Failed to upload file " + response.responseJSON.error.message); 2393 }, this); 2394 var postData; 2395 var formFiles = fileIframeForm.find("input:file"); 2396 var files = formFiles[0].files; 2397 var hasAttachment = (typeof files != "undefined"); 2398 if (hasAttachment) { 2399 // Create a formdata object and add the files 2400 var url = fileIframeForm.attr("action"); 2401 CKRte.prototype.attachFile.call(this, files, url, success, error); 2402 } 2403 evt.stop(); 2404 return false; 2405 }; 2406 2407 CKRte.prototype.handleFileUploadResponse = function(evt) { 2408 evt.stop(); 2409 var data = evt.data; 2410 var xhr = data.fileLoader.xhr; 2411 var response = xhr.responseText; 2412 response = JSON.parse(response); 2413 if(typeof(response.response) !== "object") { 2414 response.response = JSON.parse(response.response); 2415 } 2416 if (xhr.status == 200) { 2417 data.uploaded = 1; 2418 data.url = CQ.shared.HTTP.encodePath(response.response.url); 2419 data.name = response.response.properties.name; 2420 } 2421 }; 2422 2423 CKRte.prototype.attachFileFromDragAndDrop = function(evt) { 2424 var fileLoader = evt.data.fileLoader; 2425 var xhr = fileLoader.xhr; 2426 var postData; 2427 if (window.FormData) { 2428 postData = new FormData(); 2429 } 2430 if (postData) { 2431 postData.append("file", fileLoader.file); 2432 postData.append("id", "nobot"); 2433 postData.append(":operation", "social:uploadImage"); 2434 postData.append("_charset_", "UTF-8"); 2435 xhr.open("POST", fileLoader.uploadUrl, true); 2436 xhr.setRequestHeader("Accept", "application/json"); 2437 xhr.withCredentials = true; 2438 xhr.send(postData); 2439 } 2440 evt.stop(); 2441 }; 2442 2443 CKRte.prototype.attachFile = function(files, url, success, error) { 2444 var postData; 2445 if (window.FormData) { 2446 postData = new FormData(); 2447 } 2448 if (postData) { 2449 $CQ.each(files, function(key, value) { 2450 postData.append("file", value); 2451 }); 2452 postData.append("id", "nobot"); 2453 postData.append(":operation", "social:uploadImage"); 2454 postData.append("_charset_", "UTF-8"); 2455 $CQ.ajax(url, { 2456 dataType: "json", 2457 type: "POST", 2458 processData: false, 2459 contentType: false, 2460 xhrFields: { 2461 withCredentials: true 2462 }, 2463 data: postData, 2464 "success": success, 2465 "error": error 2466 }); 2467 } 2468 }; 2469 CKRte.prototype.changeImagePluginDialog = function() { 2470 // List of things being done 2471 // Get the dialog defintion of the image2 plugin 2472 // Setting a custom onClick function to the file input button in the dialog 2473 // Getting the iframe that loads the server response after an image is uploaded 2474 // Set addition params specific to upload operation 2475 // Add an onload event listener to the iframe 2476 // The iframe onload event listener updates the field that has image URL 2477 if (!CKRte.isImageDialogDefinitionChanged) { 2478 CKRte.isImageDialogDefinitionChanged = true; 2479 var that = this; 2480 window.CKEDITOR.on("dialogDefinition", function(ev) { 2481 var dialogName = ev.data.name; 2482 if (dialogName == "image2") { 2483 that.getFileIFrameFromDialog(ev.data.definition); 2484 } 2485 }); 2486 } 2487 }; 2488 2489 CKRte.prototype.config = { 2490 toolbar: [{ 2491 name: "basicstyles", 2492 items: ["Bold", "Italic", "Underline", "NumberedList", "BulletedList", "Outdent", "Indent", "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock", "TextColor"] 2493 }], 2494 autoParagraph: false, 2495 autoUpdateElement: false, 2496 removePlugins: "elementspath", 2497 resize_enabled: false 2498 }; 2499 CKRte.prototype.setValue = function(val) { 2500 this.editor.setData(val); 2501 }; 2502 CKRte.prototype.getValue = function() { 2503 return this.editor.getData(); 2504 };
2505 CKRte.prototype.focus = function() { 2506 return this.editor.focus(); 2507 }; 2508 2509 CKRte.isImageDialogDefinitionChanged = false; 2510 2511 SCF.registerFieldType("ckeditor", CKRte); 2512 SCF.registerFieldType("rte", CKRte); 2513})(Backbone, $CQ, _, Handlebars); 2514 2515/* 2516 * 2517 * ADOBE CONFIDENTIAL 2518 * __________________ 2519 * 2520 * Copyright 2012 Adobe Systems Incorporated 2521 * All Rights Reserved. 2522 * 2523 * NOTICE: All information contained herein is, and remains 2524 * the property of Adobe Systems Incorporated and its suppliers, 2525 * if any. The intellectual and technical concepts contained 2526 * herein are proprietary to Adobe Systems Incorporated and its 2527 * suppliers and are protected by trade secret or copyright law. 2528 * Dissemination of this information or reproduction of this material 2529 * is strictly forbidden unless prior written permission is obtained 2530 * from Adobe Systems Incorporated. 2531 */ 2532(function(_, $CQ, Backbone, Handlebars, SCF) { 2533 "use strict"; 2534 2535 var SmartTagManager = function(tagField, config, model) { 2536 this.containerEl = tagField; 2537 var filterVal = $CQ(tagField).data("tag-filter"); 2538 var filterLimit = $CQ(tagField).data("tag-limit"); 2539 $CQ(tagField).tagit({ 2540 fieldName: name, 2541 allowSpaces: false, 2542 placeholderText: CQ.I18n.getMessage("Add a tag"), 2543 animate: false, 2544 minLength: 2, 2545 removeConfirmation: true, 2546 showAutocompleteOnFocus: false, 2547 tagSource: function(request, response) { 2548 $CQ.ajax({ 2549 url: SCF.config.urlRoot + "/services/tagfilter", 2550 data: { 2551 term: request.term, 2552 tagfilter: filterVal, 2553 tagFilterLimit: filterLimit, 2554 pagePath: CQ.shared.HTTP.getPath(), 2555 _charset_: "UTF-8" 2556 }, 2557 dataType: "json", 2558 success: function(data) { 2559 response($CQ.map(data, function(item) { 2560 return { 2561 label: item.label, 2562 value: item.value, 2563 id: item.tagid 2564 }; 2565 })); 2566 } 2567 }); 2568 } 2569 }); 2570 2571 if (!_.isEmpty(model.get("tags"))) { 2572 $CQ.each(model.get("tags"), function(index, item) { 2573 $CQ(tagField).tagit("createTag", item.title, item.tagId, item.value); 2574 2575 }); 2576 } 2577 }; 2578 SmartTagManager.prototype.getValue = function() { 2579 var tags = []; 2580 2581 $CQ(this.containerEl).find('li').each(function() { 2582 var _liObj = $(this); 2583 var _value = _liObj.find("input").attr("value"); 2584 if (!_.isEmpty(_value)) { 2585 tags.push(_value); 2586 } 2587 }); 2588 2589 return tags; 2590 }; 2591 SmartTagManager.prototype.setValue = function() {}; 2592 SmartTagManager.prototype.focus = function() { 2593 $CQ(this.el).focus(); 2594 }; 2595 SmartTagManager.prototype.destroy = function() {}; 2596 2597 2598 2599 var TagManager = function(tagField, config, model) { 2600 var compileTemplates = function(sourceMap) { 2601 var compiledTemplates = {}; 2602 for (var key in sourceMap) { 2603 compiledTemplates[key] = Handlebars.compile(sourceMap[key]); 2604 } 2605 return compiledTemplates; 2606 }; 2607 this.modelTags = model.get("tags"); 2608 this.templatesSource = this.defaultTemplates; 2609 if (config && config.hasOwnProperty("templates")) { 2610 this.templatesSource = _.extend(this.defaultTemplates, config.templates); 2611 } 2612 this.compiledTemplates = compileTemplates(this.templatesSource); 2613 var el = tagField.get()[0]; 2614 var filterVal = $CQ(el).data("tag-filter"); 2615 var filterLimit = $CQ(el).data("tag-limit"); 2616 var tags = TagManager.tagsByFilterVal[filterVal]; 2617 if (!tags) { 2618 var that = this; 2619 $CQ.ajax({ 2620 url: SCF.config.urlRoot + "/services/tagfilter", 2621 data: { 2622 tagfilter: filterVal, 2623 tagFilterLimit: filterLimit
2624 }, 2625 // xhrFields: { 2626 // withCredentials: true 2627 // }, 2628 dataType: "json", 2629 async: false, 2630 success: function(data) { 2631 tags = data; 2632 TagManager.tagsByFilterVal[filterVal] = tags; 2633 that.initTagFields(tags, el); 2634 } 2635 }); 2636 } else { 2637 this.initTagFields(tags, el); 2638 } 2639 }; 2640 2641 TagManager.prototype.initTagFields = function(tags, field) { 2642 var tagSelector = $CQ(this.compiledTemplates.inputField(tags)); 2643 this.selectedTags = {}; 2644 var that = this; 2645 var $field = $CQ(field); 2646 $field.after(tagSelector); 2647 var attributes = $field.prop("attributes"); 2648 $CQ.each(attributes, function() { 2649 tagSelector.attr(this.name, this.value); 2650 }); 2651 tagSelector.removeAttr("data-attrib"); 2652 var selectedTags = $CQ(this.compiledTemplates.tagsContainer(this.modelTags)); 2653 2654 if (!_.isUndefined(this.modelTags) && this.modelTags !== null && this.modelTags.hasOwnProperty("length")) { 2655 for (var i = 0; i < this.modelTags.length; i++) { 2656 this.selectedTags[this.modelTags[i].tagId] = this.modelTags[i]; 2657 } 2658 } 2659 tagSelector.after(selectedTags); 2660 selectedTags.find(".scf-js-remove-tag").click(function(e) { 2661 var targetTag = $CQ(e.target).closest("[data-attrib]"); 2662 delete that.selectedTags[targetTag.attr("data-attrib")]; 2663 targetTag.remove(); 2664 }); 2665 $field.remove(); 2666 tagSelector.change(function() { 2667 $CQ(tagSelector).find("option:selected").each(function() { 2668 var tag = $CQ(this).text(); 2669 var tagId = $CQ(this).val(); 2670 $CQ(this).removeAttr("selected"); 2671 if (tagId in that.selectedTags) { 2672 return; 2673 } 2674 var selectedTag = $CQ(that.compiledTemplates.tag({ 2675 "tagid": tagId, 2676 "label": tag 2677 })); 2678 selectedTags.append(selectedTag); 2679 that.selectedTags[tagId] = tag; 2680 selectedTag.find(".scf-js-remove-tag").click(function() { 2681 selectedTag.remove(); 2682 delete that.selectedTags[tagId]; 2683 }); 2684 }); 2685 $CQ($CQ(this).find("option[disabled]")[0]).removeAttr("disabled").attr("selected", "selected").attr("disabled", "disabled"); 2686 }); 2687 }; 2688 2689 TagManager.prototype.getValue = function() { 2690 var tags = []; 2691 for (var tagId in this.selectedTags) { 2692 tags.push(tagId); 2693 } 2694 return tags; 2695 }; 2696 TagManager.prototype.setValue = function() { 2697 if (tags instanceof Array) { 2698 for (var i; i < tags.length; i++) { 2699 var tag = tags[i]; 2700 this.selectedTags[tag.tagId] = tag.title; 2701 } 2702 } 2703 }; 2704 TagManager.prototype.focus = function() { 2705 $CQ(this.el).focus(); 2706 }; 2707 TagManager.prototype.destroy = function() {}; 2708 2709 TagManager.prototype.defaultTemplates = { 2710 "inputField": "<select size=\"1\"><option disabled selected>add a tag</option>{{#each this}}<option value=\"{{tagid}}\">{{label}}</option>{{/each}}</select>", 2711 "tagsContainer": "<ul class=\"scf-horizontal-tag-list\">{{#each this}}<li class=\"scf-selected-tag \" data-attrib=\"{{tagId}}\"><span class=\"scf-js-remove-tag scf-remove-tag\"></span> {{title}}</li>{{/each}}</div>", 2712 "tag": "<li class=\"scf-selected-tag \"><span class=\"scf-js-remove-tag scf-remove-tag\"></span> {{label}}</li>" 2713 }; 2714 2715 TagManager.tagsByFilterVal = {}; 2716 2717 SCF.registerFieldType("tags", TagManager); 2718 SCF.registerFieldType("smarttags", SmartTagManager); 2719 // Maybe this export can be removed when we transition over totally to SCF 2720 SCF.TagManager = TagManager; 2721 SCF.SmartTagManager = SmartTagManager; 2722 2723})(_, $CQ, Backbone, Handlebars, SCF); 2724 2725/* 2726 * 2727 * ADOBE CONFIDENTIAL 2728 * __________________ 2729 * 2730 * Copyright 2012 Adobe Systems Incorporated 2731 * All Rights Reserved. 2732 *
2733 * NOTICE: All information contained herein is, and remains 2734 * the property of Adobe Systems Incorporated and its suppliers, 2735 * if any. The intellectual and technical concepts contained 2736 * herein are proprietary to Adobe Systems Incorporated and its 2737 * suppliers and are protected by trade secret or copyright law. 2738 * Dissemination of this information or reproduction of this material 2739 * is strictly forbidden unless prior written permission is obtained 2740 * from Adobe Systems Incorporated. 2741 */ 2742(function(_, $CQ, Backbone, Handlebars, SCF) { 2743 "use strict"; 2744 2745 // select user 2746 var initUserSelector = function(el_selector, path, dropDown) { 2747 var $el = $CQ(el_selector); 2748 // userlist node needs to be next to ugc node 2749 var base_url = path; 2750 $el.autocomplete({ 2751 source: function(request, response) { 2752 var searchString = $CQ(el_selector).val(); 2753 var filterObject = { 2754 "operation": "CONTAINS", 2755 "./@rep:principalName": searchString 2756 }; 2757 filterObject = [filterObject]; 2758 var filterGivenName = { 2759 "operation": "like", 2760 "profile/@givenName": searchString 2761 }; 2762 filterObject.push(filterGivenName); 2763 var filterFamilyName = { 2764 "operation": "like", 2765 "profile/@familyName": searchString 2766 }; 2767 filterObject.push(filterFamilyName); 2768 filterObject = JSON.stringify(filterObject); 2769 2770 var sitePath = SCF.Context.sitePath + "/configuration.social.0.20.json"; 2771 $CQ.ajax({ 2772 url: sitePath, 2773 type: "GET", 2774 success: function(siteJson) { 2775 var url = base_url + ".social.0.20.json"; 2776 url = CQ.shared.HTTP.addParameter(url, "filter", filterObject); 2777 url = CQ.shared.HTTP.addParameter(url, "type", "users"); 2778 url = CQ.shared.HTTP.addParameter(url, "fromPublisher", "true"); 2779 url = CQ.shared.HTTP.addParameter(url, "_charset_", "utf-8"); 2780 url = CQ.shared.HTTP.addParameter(url, "groupId", "community-" + siteJson.siteId + "-members"); 2781 $.get(url, function(data) { 2782 var users = data.items; 2783 $el.data("lastQueryResult", users); 2784 response(users); 2785 }); 2786 } 2787 }); 2788 }, 2789 minLength: 3, 2790 change: function(event, ui) { 2791 dropDown.model.set("composedForValid", dropDown.validateUser($el.val())); 2792 }, 2793 select: function(event, ui) { 2794 dropDown.model.set("composedForValid", true); 2795 $CQ(this).val(ui.item.authorizableId); 2796 return false; 2797 }, 2798 setvalue: function(value) { 2799 this.element.val(value); 2800 this.input.val(value); 2801 $CQ(this).val(value); 2802 } 2803 }).data("uiAutocomplete")._renderItem = function(ul, item) { 2804 if (item.avatarUrl) { 2805 return $CQ("<li></li>").append( 2806 "<a><img src='" + item.avatarUrl + "' width='30' height='30'/> " + item.name + "</a>") 2807 .data("item.autocomplete", item).appendTo(ul); 2808 } else { 2809 return $CQ("<li></li>").append("<a>" + item.name + "</a>").data("item.autocomplete", item).appendTo(ul); 2810 } 2811 }; 2812 }; 2813 2814 var UserDropDown = function(inputEl, config, model) { 2815 this.$el = $CQ(inputEl); 2816 this.model = model; 2817 this.config = config; 2818 this.modelId = this.model.get("forumId"); 2819 if (_.isEmpty(this.modelId)) { 2820 this.modelId = this.model.get("id"); 2821 } 2822 initUserSelector($CQ(inputEl), this.modelId + "/userlist", this); 2823 }; 2824 2825 var isUserInList = function(userList, userName) { 2826 for (var user in userList) { 2827 if (userList[user].authorizableId === userName) { 2828 return true; 2829 } 2830 } 2831 return false;
2832 }; 2833 2834 UserDropDown.prototype.validateUser = function(userName) { 2835 var isValid = false; 2836 2837 // Check to see if the user is blank 2838 if (userName.trim().length === 0) 2839 return true; 2840 2841 // First, check last (cached) search, if it exists 2842 if (this.$el.data("lastQueryResult")) { 2843 if (isUserInList(this.$el.data("lastQueryResult"), userName)) { 2844 isValid = true; 2845 } 2846 } 2847 // Next, perform a query and check to see if we find a match 2848 if (!isValid) { 2849 var users = this.searchUsers(userName); 2850 isValid = isUserInList(users, userName); 2851 } 2852 return isValid; 2853 }; 2854 2855 UserDropDown.prototype.searchUsers = function(userName) { 2856 var base_url = this.modelId + "/userlist"; 2857 var url = base_url + ".social.0.20.json?search=" + userName + "&showUsers=true"; 2858 var users; 2859 $.get(url, function(data) { 2860 users = data.items; 2861 }); 2862 users = users || []; 2863 return users; 2864 }; 2865 2866 UserDropDown.prototype.getValue = function() { 2867 return this.$el.val(); 2868 }; 2869 2870 UserDropDown.prototype.setValue = function() { 2871 // Some model prop 2872 this.$el.autocomplete().setValue(this.model.get("author").id); 2873 }; 2874 2875 UserDropDown.prototype.focus = function() { 2876 $CQ(this.el).focus(); 2877 }; 2878 2879 UserDropDown.prototype.destroy = function() { 2880 if (this.$el.data('autocomplete') || this.$el.data('lastQueryResult')) { 2881 this.$el.autocomplete("destroy"); 2882 } 2883 }; 2884 2885 SCF.registerFieldType("userdropdown", UserDropDown); 2886 SCF.UserDropDown = UserDropDown; 2887})(_, $CQ, Backbone, Handlebars, SCF); 2888
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.