fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2012-03-13 10:30 am

[dw-free] jQuerify inline entry tracking

[commit: http://hg.dwscoalition.org/dw-free/rev/9970a9c7dc2f]

http://bugs.dwscoalition.org/show_bug.cgi?id=3673

jQuerify the tracking popup bits of esn.js, so we can have inline tracking
in s2 journal views. This also makes the (jquerified) ESN javascript only be
loaded onto the page if we need it, not everywhere.

Patch by [personal profile] fu based on work by [personal profile] yvi.

Files modified:
  • cgi-bin/LJ/S2.pm
  • cgi-bin/LJ/S2/EntryPage.pm
  • cgi-bin/LJ/S2/FriendsPage.pm
  • cgi-bin/LJ/S2/RecentPage.pm
  • cgi-bin/LJ/S2/ReplyPage.pm
  • htdocs/js/dw/dw-core.js
  • htdocs/js/jquery.ajaxtip.js
  • htdocs/js/jquery.esn.js
  • htdocs/stc/esn.css
--------------------------------------------------------------------------------
diff -r 52f1f972f400 -r 9970a9c7dc2f cgi-bin/LJ/S2.pm
--- a/cgi-bin/LJ/S2.pm	Tue Mar 13 18:21:03 2012 +0800
+++ b/cgi-bin/LJ/S2.pm	Tue Mar 13 18:31:54 2012 +0800
@@ -1789,6 +1789,22 @@
     return $reparse_userprop->();
 }
 
+sub tracking_popup_js {
+    return LJ::is_enabled( 'esn_ajax' ) ? (
+        { group => 'jquery' }, qw(
+        js/jquery/jquery.ui.widget.js
+
+        js/jquery.ajaxtip.js
+        js/tooltip.js
+        js/jquery/jquery.ui.position.js
+        stc/ajaxtip.css
+
+        js/jquery.esn.js
+        stc/esn.css
+    ) ): ();
+}
+
+
 ## S2 object constructors
 
 sub CommentInfo
diff -r 52f1f972f400 -r 9970a9c7dc2f cgi-bin/LJ/S2/EntryPage.pm
--- a/cgi-bin/LJ/S2/EntryPage.pm	Tue Mar 13 18:21:03 2012 +0800
+++ b/cgi-bin/LJ/S2/EntryPage.pm	Tue Mar 13 18:31:54 2012 +0800
@@ -407,6 +407,7 @@
             stc/ajaxtip.css
             stc/popup-form.css
         ) );
+    LJ::need_res( LJ::S2::tracking_popup_js() );
 
     $p->{'_picture_keyword'} = $get->{'prop_picture_keyword'};
 
diff -r 52f1f972f400 -r 9970a9c7dc2f cgi-bin/LJ/S2/FriendsPage.pm
--- a/cgi-bin/LJ/S2/FriendsPage.pm	Tue Mar 13 18:21:03 2012 +0800
+++ b/cgi-bin/LJ/S2/FriendsPage.pm	Tue Mar 13 18:31:54 2012 +0800
@@ -33,6 +33,8 @@
     # Add a friends-specific XRDS reference
     $p->{'head_content'} .= qq{<meta http-equiv="X-XRDS-Location" content="}.LJ::ehtml($u->journal_base).qq{/data/yadis/friends" />\n};
 
+    LJ::need_res( LJ::S2::tracking_popup_js() );
+
     # load for ajax cuttag
     LJ::need_res( 'js/cuttag-ajax.js' );
     LJ::need_res( { group => "jquery" }, qw(
diff -r 52f1f972f400 -r 9970a9c7dc2f cgi-bin/LJ/S2/RecentPage.pm
--- a/cgi-bin/LJ/S2/RecentPage.pm	Tue Mar 13 18:21:03 2012 +0800
+++ b/cgi-bin/LJ/S2/RecentPage.pm	Tue Mar 13 18:31:54 2012 +0800
@@ -88,6 +88,8 @@
         $p->{'head_content'} .= qq{<meta name="ICBM" content="$icbm" />\n};
     }
 
+    LJ::need_res( LJ::S2::tracking_popup_js() );
+
     # load for ajax cuttag
     LJ::need_res( 'js/cuttag-ajax.js' );
     LJ::need_res( { group => "jquery" }, qw(
diff -r 52f1f972f400 -r 9970a9c7dc2f cgi-bin/LJ/S2/ReplyPage.pm
--- a/cgi-bin/LJ/S2/ReplyPage.pm	Tue Mar 13 18:21:03 2012 +0800
+++ b/cgi-bin/LJ/S2/ReplyPage.pm	Tue Mar 13 18:31:54 2012 +0800
@@ -61,6 +61,7 @@
     $p->{'head_content'} .= $LJ::COMMON_CODE{'chalresp_js'};
 
     LJ::need_res('stc/display_none.css');
+    LJ::need_res( LJ::S2::tracking_popup_js() );
     
     # libs for userpicselect
     my $beta = LJ::BetaFeatures->user_in_beta( $remote => "journaljquery" );
diff -r 52f1f972f400 -r 9970a9c7dc2f htdocs/js/dw/dw-core.js
--- a/htdocs/js/dw/dw-core.js	Tue Mar 13 18:21:03 2012 +0800
+++ b/htdocs/js/dw/dw-core.js	Tue Mar 13 18:31:54 2012 +0800
@@ -81,4 +81,12 @@
     return $this;
 };
 
+Unique = {
+    count: 0,
+
+    id: function() {
+        return ++this.count;
+    }
+}
+
 ;
diff -r 52f1f972f400 -r 9970a9c7dc2f htdocs/js/jquery.ajaxtip.js
--- a/htdocs/js/jquery.ajaxtip.js	Tue Mar 13 18:21:03 2012 +0800
+++ b/htdocs/js/jquery.ajaxtip.js	Tue Mar 13 18:31:54 2012 +0800
@@ -4,7 +4,8 @@
         namespace: undefined,
         content: undefined,
         tooltip: { dynamic: true },
-        persist: false
+        persist: false,
+        multiple: false // allow multiple ajaxtip requests, even if we're not done processing the previous
     },
     _namespace: function() {
         return this.options.namespace ? "."+this.options.namespace : "";
@@ -103,7 +104,7 @@
         var self = this;
 
         var tip = self.element.data("tooltip");
-        if( tip ) {
+        if( tip && ! opts.multiple ) {
             if( tip.inprogress ) return;
             if( tip.isShown() ) tip.hide();
             tip.inprogress = true;
diff -r 52f1f972f400 -r 9970a9c7dc2f htdocs/js/jquery.esn.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/js/jquery.esn.js	Tue Mar 13 18:31:54 2012 +0800
@@ -0,0 +1,290 @@
+(function($,Site,cmtinfo){
+$.widget("dw.trackbutton", {
+options: {},
+_toggleSubscriptions: function(subInfo,subs) {
+    var self = this;
+    subInfo["subid"] = Number(subInfo["subid"]);
+
+    if ((subInfo["subid"] && ! subs["newComments"])
+        || (! subInfo["subid"] && subs["newComments"])) {
+        self._toggleSubscription(subInfo, "newComments");
+    }
+
+    subInfo["newentry_subid"] = Number(subInfo["newentry_subid"]);
+    if ((subInfo["newentry_subid"] && ! subs["newEntry"])
+        || (! subInfo["newentry_subid"] && subs["newEntry"])) {
+
+        var newEntrySubInfo = new Object(subInfo);
+        newEntrySubInfo["subid"] = Number(subInfo["newentry_subid"]);
+        self._toggleSubscription(newEntrySubInfo, "newEntry");
+    }
+},
+
+_toggleSubscription: function(subInfo, type) {
+    var self = this;
+
+    var action;
+    var params = {
+        auth_token: type === "newEntry" ? subInfo.newentry_token : subInfo.auth_token
+    };
+
+    if (Number(subInfo.subid)) {
+        // subscription exists
+        action = "delsub";
+        params.subid = subInfo.subid;
+    } else {
+        // create a new subscription
+        action = "addsub";
+
+        var param_keys;
+        if (type === "newEntry") {
+            params.etypeid = subInfo.newentry_etypeid;
+            param_keys = ["journalid"];
+        } else {
+            param_keys = ["journalid", "arg1", "arg2", "etypeid"];
+        }
+
+        // get necessary AJAX parameters
+        $.each(param_keys,(function (index,param) {
+            if (Number(subInfo[param]))
+                params[param] = parseInt(subInfo[param]);
+        }));
+    }
+    params.action = action;
+
+    var $clicked = self.element;
+    $clicked.ajaxtip({namespace: "trackbutton"})
+        .ajaxtip("load", {
+            endpoint: "esn_subs",
+            context: self,
+            data: params,
+            multiple: true,
+            success: function( data, status, jqxhr ) {
+                if (data.error) {
+                    $clicked.ajaxtip("error", data.error);
+                } else if (data.success) {
+                    if (data.msg)
+                        $clicked.ajaxtip("success", data.msg);
+
+                    if (data.subscribed) {
+                        if (data.subid)
+                            $clicked.attr("lj_subid", data.subid);
+                        if (data.newentry_subid)
+                            $clicked.attr("lj_newentry_subid", data.newentry_subid);
+                    } else {  // deleted subscription
+                        if (data.event_class == "LJ::Event::JournalNewComment")
+                            $clicked.attr("lj_subid", 0);
+                        if (data.event_class == "LJ::Event::JournalNewEntry")
+                            $clicked.attr("lj_newentry_subid", 0);
+                    }
+                    if (data.auth_token)
+                        $clicked.attr("lj_auth_token", data.auth_token);
+                    if (data.newentry_token)
+                        $clicked.attr("lj_newentry_token", data.newentry_token);
+
+
+                    var state = data.subscribed ? "on" : "off";
+
+                    // update tracking icons if we've modified tracking for comments
+                    // to this entry (versus new entries to this journal)
+                    if ( data.event_class == "LJ::Event::JournalNewComment" ) {
+                        var dtalkid = $clicked.attr("lj_dtalkid");
+                        if ( dtalkid ) {
+                            if (!data.subscribed) {
+                                // show new state of this comment:
+                                // "off" by default if no parents are being tracked,
+                                // otherwise set state to "parent", which is equivalent to "on"
+                                var $parentBtn;
+                                var parent_dtalkid = dtalkid;
+                                var cmtInfo = cmtinfo[dtalkid+""];
+
+                                while ( $parentBtn = self._getParentButton(parent_dtalkid) ) {
+                                    parent_dtalkid = $parentBtn.attr("lj_dtalkid");
+                                    if ( ! parent_dtalkid ) break;
+
+                                    if (! Number($parentBtn.attr("lj_subid"))) continue;
+                                    state = "parent";
+                                    break;
+                                }
+                            }
+                            self._updateThread(dtalkid, state);
+                        } else {
+                            self._updateButton(self.element,state);
+                        }
+                    }
+                }
+
+                self._trigger( "complete" );
+            }
+        });
+
+},
+
+// given a dtalkid, find the track button for its parent comment (if any)
+_getParentButton: function(dtalkid) {
+    var cmt = cmtinfo[dtalkid+""];
+    if ( ! cmt ) return null;
+
+    var parent_dtalkid = cmt.parent;
+    if ( ! parent_dtalkid ) return null;
+
+    return $("#lj_track_btn_" + parent_dtalkid);
+},
+
+_updateButton: function($button,state) {
+    var uri;
+    switch(state) {
+        case "on":
+        case "parent":
+            uri = "/silk/entry/untrack.png";
+            break;
+        case "off":
+            uri = "/silk/entry/track.png";
+            break;
+        default:
+            alert("Unknown tracking state " + state);
+            break;
+    }
+
+    if ( $button.has("img") ) {
+        $button.find("img").attr("src", Site.imgprefix + uri);
+    } else {
+        var swapName = $button.html();
+        $button.html($button.attr("js_swapname"));
+        $button.attr("js_swapname", swapName);
+    }
+},
+
+_updateThread: function(dtalkid, state) {
+    var self = this;
+    var $btn = $("#lj_track_btn_" + dtalkid);
+    if ( ! $btn.length ) return;
+
+    var cmtInfo = cmtinfo[dtalkid + ""];
+    if (! cmtInfo) return;
+
+    // subscription already exists on this button, don't mess with it
+    if (Number($btn.attr("lj_subid")) && state != "on")
+        return;
+
+    if (cmtInfo.rc && cmtInfo.rc.length) {
+        // update children
+        $.each(cmtInfo.rc, function (i,child_dtalkid) {
+            window.setTimeout(function () {
+                var threadState;
+                switch (state) {
+                case "on":
+                    threadState = "parent";
+                    break;
+                case "off":
+                    threadState = "off";
+                    break;
+                case "parent":
+                    threadState = "parent";
+                    break;
+                default:
+                    $btn.ajaxtip("error", "Unknown tracking state " + state)
+                    break;
+                }
+                self._updateThread(child_dtalkid, threadState);
+            }, 300);
+        });
+    }
+
+    self._updateButton($btn,state);
+},
+
+_create: function() {
+    if (! Site || ! Site.has_remote) return;
+
+    var self = this;
+    var $ele = self.element;
+    if ($ele.attr("lj_subid") === undefined || $ele.attr("lj_journalid") === undefined ) return;
+
+    $ele.click( function(e) {
+        // don't show the popup if we want to open it in a new tab (ctrl+click or cmd+click)
+        if (e.ctrlKey || e.metaKey) return;
+
+        e.preventDefault();
+        e.stopPropagation();
+
+        var btnInfo = {};
+        var args = ['arg1', 'arg2', 'etypeid', 'newentry_etypeid', 'newentry_token', 'newentry_subid',
+         'journalid', 'subid', 'auth_token'];
+        $.each( args, function (index, arg) {
+            btnInfo[arg] = $ele.attr("lj_" + arg);
+        });
+
+        // show the menu
+        $ele.ajaxtip({
+            namespace:"trackbutton",
+            content: function() {
+
+            // the dialog we will show later
+            var $dlg = $("<div class='trackdialog'><div class='track_title'>Email me when</div></div>");
+
+            // this creates a new checkbox for a notification option with a specified name
+            // and default check option
+            var TrackCheckbox = function (title, checked) {
+                var uniqueid = "newentrytrack" + Unique.id();
+                var $checkbox = $("<input></input>",
+                    { "type": "checkbox", "id": uniqueid, "checked": checked });
+                var $checkContainer = $("<div></div>")
+                    .append( $checkbox, $("<label></label>", { "for": uniqueid }).html(title) )
+                    .appendTo( $dlg );
+
+                return $checkbox;
+            };
+
+            // is the user already tracking new entries by this user / new comments on this entry?
+            var trackingNewEntries  = Number(btnInfo['newentry_subid']) ? true : false;
+            var trackingNewComments = Number(btnInfo['subid']) ? true : false;
+
+            var $newEntryTrackBtn;
+            var $commentsTrackBtn;
+
+            if (Number($ele.attr("lj_dtalkid"))) {
+                // this is a thread tracking button
+                // always checked: either because they're subscribed, or because
+                // they're going to subscribe.
+                $commentsTrackBtn = TrackCheckbox("someone replies in this comment thread", true);
+            } else {
+                // entry tracking button
+                var journal = cmtinfo["journal"] || $ele.attr("journal") || Site.currentJournal;
+                if ( journal ) {
+                    $newEntryTrackBtn = TrackCheckbox( journal + " posts a new entry", trackingNewEntries );
+                }
+                $commentsTrackBtn = TrackCheckbox("someone comments on this post", trackingNewComments);
+            }
+
+            var $savebutton = $("<input type='button' class='track_savechanges' value='Save Changes'></input>")
+                .click(function() {
+                    self._toggleSubscriptions(btnInfo,{
+                        newEntry: $newEntryTrackBtn ? $newEntryTrackBtn.is(":checked") : false,
+                        newComments: $commentsTrackBtn.is(":checked")
+                    });
+                });
+            var $btnsContainer = $("<div class='track_btncontainer'></div>")
+                .append( $savebutton,
+                    $("<a></a>", {"href": $ele.attr("href"), "class": "track_moreopts"}).html("More Options")
+                ).appendTo($dlg);
+
+            return $dlg;
+            }
+
+        })
+    });
+
+}
+});
+
+
+})(jQuery,window.Site||{},window.LJ_cmtinfo||{});
+
+jQuery(function($){
+    $("a.TrackButton").trackbutton();
+    $(document.body).delegate("*","updatedcontent.comment", function(e) {
+        e.stopPropagation();
+        $("a.TrackButton",this).trackbutton();
+    });
+});
diff -r 52f1f972f400 -r 9970a9c7dc2f htdocs/stc/esn.css
--- a/htdocs/stc/esn.css	Tue Mar 13 18:21:03 2012 +0800
+++ b/htdocs/stc/esn.css	Tue Mar 13 18:31:54 2012 +0800
@@ -268,23 +268,23 @@
 
 
 /**** ESN AJAX ****/
-.ippu .track_title {
+.trackdialog .track_title, .ippu .track_title {
     font-weight: bold;
     margin: 4px;
 }
 
-.ippu .track_btncontainer {
+.trackdialog .track_btncontainer, .ippu .track_btncontainer {
     margin-top: 5px;
     padding: 1px;
     width: 250px;
 }
 
-.ippu .track_moreopts {
+.trackdialog .track_moreopts, .ippu .track_moreopts {
     margin: auto auto auto 1em;
     width: 49%;
 }
 
-.ippu .track_savechanges {
+.trackdialog .track_savechanges, .ippu .track_savechanges {
     margin: auto auto auto auto;
     width: 49%;
 }
--------------------------------------------------------------------------------