PageSourceSearch

https://ncmec.org/etc.clientlibs/clientlibs/social/commons/scf.js

js ncmec.org collected 2026-10-01 07:13:23 UTC 113,748 bytes, 2,888 lines download raw bytes

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'/>&nbsp;" + 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.