PageSourceSearch

https://www.bayareaeconomy.org/wp-content/plugins/select-to-share/assets/js/select-to-share.js?ver=1.0.1

js bayareaeconomy.org collected 2026-10-02 04:55:56 UTC 14,089 bytes, 359 lines download raw bytes

1/*
2 * share-selection: Medium like popover menu to share on Twitter or by email any text selected on the page
3 *
4 * -- Requires jQuery --
5 * -- AMD compatible  --
6 *
7 * Author: Xavier Damman (@xdamman)
8 * GIT: https://github.com/xdamman/share-selection
9 * MIT License
10 */
11
12(function($) {
13
14  var SelectionSharer = function(options) {
15
16    var self = this;
17
18    options = options || {};
19    if(typeof options == 'string')
20        options = { elements: options };
21
22    this.sel = null;
23    this.textSelection='';
24    this.htmlSelection='';
25
26    this.appId = $('meta[property="fb:app_id"]').attr("content") || $('meta[property="fb:app_id"]').attr("value");
27    this.url2share = $('meta[property="og:url"]').attr("content") || $('meta[property="og:url"]').attr("value") || window.location.href;
28
29    this.getSelectionText = function(sel) {
30        var html = "", text = "";
31        var sel = sel || window.getSelection();
32        if (sel.rangeCount) {
33            var container = document.createElement("div");
34            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
35                container.appendChild(sel.getRangeAt(i).cloneContents());
36            }
37            text = container.textContent;
38            html = container.innerHTML
39        }
40        self.textSelection = text;
41        self.htmlSelection = html || text;
42        return text;
43    };
44
45    this.selectionDirection = function(selection) {
46      var sel = selection || window.getSelection();
47      var range = document.createRange();
48      if(!sel.anchorNode) return 0;
49      range.setStart(sel.anchorNode, sel.anchorOffset);
50      range.setEnd(sel.focusNode, sel.focusOffset);
51      var direction = (range.collapsed) ? "backward" : "forward";
52      range.detach();
53      return direction;
54    };
55
56    this.showPopunder = function() {
57      self.popunder = self.popunder || document.getElementById('selectionSharerPopunder');
58
59      var sel = window.getSelection();
60      var selection = self.getSelectionText(sel);
61
62      if(sel.isCollapsed || selection.length < 10 || !selection.match(/ /))
63        return self.hidePopunder();
64
65      if(self.popunder.classList.contains("fixed"))
66        return self.popunder.style.bottom = 0;
67
68      var range = sel.getRangeAt(0);
69      var node = range.endContainer.parentNode; // The <p> where the selection ends
70
71      // If the popunder is currently displayed
72      if(self.popunder.classList.contains('show')) {
73        // If the popunder is already at the right place, we do nothing
74        if(Math.ceil(self.popunder.getBoundingClientRect().top) == Math.ceil(node.getBoundingClientRect().bottom))
75          return;
76
77        // Otherwise, we first hide it and the we try again
78        return self.hidePopunder(self.showPopunder);
79      }
80
81      if(node.nextElementSibling) {
82        // We need to push down all the following siblings
83        self.pushSiblings(node);
84      }
85      else {
86        // We need to append a new element to push all the content below
87        if(!self.placeholder) {
88          self.placeholder = document.createElement('div');
89          self.placeholder.className = 'selectionSharerPlaceholder';
90        }
91
92        // If we add a div between two <p> that have a 1em margin, the space between them
93        // will become 2x 1em. So we give the placeholder a negative margin to avoid that
94        var margin = window.getComputedStyle(node).marginBottom;
95        self.placeholder.style.height = margin;
96        self.placeholder.style.marginBottom = (-2 * parseInt(margin,10))+'px';
97        node.parentNode.insertBefore(self.placeholder);
98      }
99
100      // scroll offset
101      var offsetTop = window.pageYOffset + node.getBoundingClientRect().bottom;
102      self.popunder.style.top = Math.ceil(offsetTop)+'px';
103
104      setTimeout(function() {
105        if(self.placeholder) self.placeholder.classList.add('show');
106        self.popunder.classList.add('show');
107      },0);
108
109    };
110
111    this.pushSiblings = function(el) {
112      while(el=el.nextElementSibling) { el.classList.add('selectionSharer'); el.classList.add('moveDown'); }
113    };
114
115    this.hidePopunder = function(cb) {
116      cb = cb || function() {};
117
118      if(self.popunder == "fixed") {
119        self.popunder.style.bottom = '-50px';
120        return cb();
121      }
122
123      self.popunder.classList.remove('show');
124      if(self.placeholder) self.placeholder.classList.remove('show');
125      // We need to push back up all the siblings
126      var els = document.getElementsByClassName('moveDown');
127      while(el=els[0]) {
128          el.classList.remove('moveDown');
129      }
130
131      // CSS3 transition takes 0.6s
132      setTimeout(function() {
133        if(self.placeholder) document.body.insertBefore(self.placeholder);
134        cb();
135      }, 600);
136
137    };
138
139    this.show = function(e) {
140      setTimeout(function() {
141        var sel = window.getSelection();
142        var selection = self.getSelectionText(sel);
143        if(!sel.isCollapsed && selection && selection.length>10 && selection.match(/ /)) {
144          var range = sel.getRangeAt(0);
145          var topOffset = range.getBoundingClientRect().top - 5;
146          var top = topOffset + window.scrollY - self.$popover.height();
147          var left = 0;
148          if(e) {
149            left = e.pageX;
150          }
151          else {
152            var obj = sel.anchorNode.parentNode;
153            left += obj.offsetWidth / 2;
154            do {
155              left += obj.offsetLeft;
156            }
157            while(obj = obj.offsetParent);
158          }
159          switch(self.selectionDirection(sel)) {
160            case 'forward':
161              left -= self.$popover.width();
162              break;
163            case 'backward':
164              left += self.$popover.width();
165              break;
166            default:
167              return;
168          }
169          self.$popover.removeClass("anim").css("top", top+10).css("left", left).show();
170          setTimeout(function() {
171            self.$popover.addClass("anim").css("top", top);
172          }, 0);
173        }
174      }, 10);
175    };
176
177    this.hide = function(e) {
178      self.$popover.hide();
179    };
180
181    this.smart_truncate = function(str, n){
182        if (!str || !str.length) return str;
183        var toLong = str.length>n,
184            s_ = toLong ? str.substr(0,n-1) : str;
185        s_ = toLong ? s_.substr(0,s_.lastIndexOf(' ')) : s_;
186        return  toLong ? s_ +'...' : s_;
187    };
188
189    this.getRelatedTwitterAccounts = function() {
190      var usernames = [];
191
192      var creator = $('meta[name="twitter:creator"]').attr("content") || $('meta[name="twitter:creator"]').attr("value");
193      if(creator) usernames.push(creator);
194
195
196      // We scrape the page to find a link to http(s)://twitter.com/username
197      var anchors = document.getElementsByTagName('a');
198      for(var i=0, len=anchors.length;i<len;i++) {
199        if(anchors[i].attributes.href && typeof anchors[i].attributes.href.value == 'string') {
200          var matches = anchors[i].attributes.href.value.match(/^https?:\/\/twitter\.com\/([a-z0-9_]{1,20})/i)
201          if(matches && matches.length > 1 && ['widgets','intent'].indexOf(matches[1])==-1)
202            usernames.push(matches[1]);
203        }
204      }
205
206      if(usernames.length > 0)
207        return usernames.join(',');
208      else
209        return '';
210    };
211
212    this.shareTwitter = function(e) {
213      e.preventDefault();
214
215      var text = "“"+self.smart_truncate(self.textSelection.trim(), 114)+"”";
216      var url = 'http://twitter.com/intent/tweet?text='+encodeURIComponent(text)+'&related='+self.relatedTwitterAccounts+'&url='+encodeURIComponent(window.location.href);
217
218      // We only show the via @twitter:site if we have enough room
219      if(self.viaTwitterAccount && text.length < (120-6-self.viaTwitterAccount.length))
220        url += '&via='+self.viaTwitterAccount;
221
222      var w = 640, h=440;
223      var left = (screen.width/2)-(w/2);
224      var top = (screen.height/2)-(h/2)-100;
225      window.open(url, "share_twitter", 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left);
226      self.hide();
227      return false;
228    };
229
230    this.shareFacebook = function(e) {
231      e.preventDefault();
232      var text = self.htmlSelection.replace(/<p[^>]*>/ig,'\n').replace(/<\/p>|  /ig,'').trim();
233
234      var url = 'https://www.facebook.com/dialog/feed?' +
235                'app_id='+self.appId +
236                '&display=popup'+
237                '&caption='+encodeURIComponent(text)+
238                '&link='+encodeURIComponent(self.url2share)+
239                '&href='+encodeURIComponent(self.url2share)+
240                '&redirect_uri='+encodeURIComponent(self.url2share);
241      var w = 640, h=440;
242      var left = (screen.width/2)-(w/2);
243      var top = (screen.height/2)-(h/2)-100;
244
245      window.open(url, "share_facebook", 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left);
246    };
247
248    this.shareEmail = function(e) {
249      var text = self.textSelection.replace(/<p[^>]*>/ig,'\n').replace(/<\/p>|  /ig,'').trim();
250      var email = {};
251      email.subject = encodeURIComponent("Quote from "+document.title);
252      email.body = encodeURIComponent("“"+text+"”")+"%0D%0A%0D%0AFrom: "+document.title+"%0D%0A"+window.location.href;
253      $(this).attr("href","mailto:?subject="+email.subject+"&body="+email.body);
254      self.hide();
255      return true;
256    };
257
258    this.render = function() {
259      var popoverHTML =  '<div class="selectionSharer" id="selectionSharerPopover" style="position:absolute;">'
260                       + '  <div id="selectionSharerPopover-inner">'
261                       + '    <ul>'
262                       + '      <li><a class="action tweet" href="" title="Share this selection on Twitter" target="_blank">Tweet</a></li>'
263                       + '      <li><a class="action facebook" href="" title="Share this selection on Facebook" target="_blank">Facebook</a></li>'
264                       + '      <li><a class="action email" href="" title="Share this selection by email" target="_blank"><svg width="20" height="20"><path stroke="#FFF" stroke-width="6" d="m16,25h82v60H16zl37,37q4,3 8,0l37-37M16,85l30-30m22,0 30,30"/></svg></a></li>'
265                       + '    </ul>'
266                       + '  </div>'
267                       + '  <div class="selectionSharerPopover-clip"><span class="selectionSharerPopover-arrow"></span></div>'
268                       + '</div>';
269
270      var popunderHTML = '<div id="selectionSharerPopunder" class="selectionSharer">'
271                       + '  <div id="selectionSharerPopunder-inner">'
272                       + '    <label>Share this selection</label>'
273                       + '    <ul>'
274                       + '      <li><a class="action tweet" href="" title="Share this selection on Twitter" target="_blank">Tweet</a></li>'
275                       + '      <li><a class="action facebook" href="" title="Share this selection on Facebook" target="_blank">Facebook</a></li>'
276                       + '      <li><a class="action email" href="" title="Share this selection by email" target="_blank"><svg width="20" height="20"><path stroke="#FFF" stroke-width="6" d="m16,25h82v60H16zl37,37q4,3 8,0l37-37M16,85l30-30m22,0 30,30"/></svg></a></li>'
277                       + '    </ul>'
278                       + '  </div>'
279                       + '</div>';
280      self.$popover = $(popoverHTML);
281      self.$popover.find('a.tweet').click(self.shareTwitter);
282      self.$popover.find('a.facebook').click(self.shareFacebook);
283      self.$popover.find('a.email').click(self.shareEmail);
284
285      $('body').append(self.$popover);
286
287      self.$popunder = $(popunderHTML);
288      self.$popunder.find('a.tweet').click(self.shareTwitter);
289      self.$popunder.find('a.facebook').click(self.shareFacebook);
290      self.$popunder.find('a.email').click(self.shareEmail);
291      $('body').append(self.$popunder);
292
293      if (self.appId && self.url2share){
294        $(".selectionSharer a.facebook").css('display','inline-block');
295      }
296    };
297
298    this.setElements = function(elements) {
299      if(typeof elements == 'string') elements = $(elements);
300      self.$elements = elements instanceof $ ? elements : $(elements);
301      self.$elements.mouseup(self.show).mousedown(self.hide).addClass("selectionShareable");
302
303      self.$elements.bind('touchstart', function(e) {
304        self.isMobile = true;
305      });
306
307      document.onselectionchange = self.selectionChanged;
308    };
309
310    this.selectionChanged = function(e) {
311      if(!self.isMobile) return;
312
313      if(self.lastSelectionChanged) {
314        clearTimeout(self.lastSelectionChanged);
315      }
316      self.lastSelectionChanged = setTimeout(function() {
317        self.showPopunder(e);
318      }, 300);
319    };
320
321    this.render();
322
323    if(options.elements) {
324      this.setElements(options.elements);
325    }
326
327  };
328
329  // jQuery plugin
330  // Usage: $( "p" ).selectionSharer();
331  $.fn.selectionSharer = function() {
332    var sharer = new SelectionSharer();
333    sharer.setElements(this);
334    return this;
335  };
336
337  // For AMD / requirejs
338  // Usage: require(["selection-sharer!"]);
339  //     or require(["selection-sharer"], function(selectionSharer) { var sharer = new SelectionSharer('p'); });
340  if(typeof define == 'function') {
341    define(function() {
342      SelectionSharer.load = function (name, req, onLoad, config) {
343        var sharer = new SelectionSharer();
344        sharer.setElements('p');
345        onLoad();
346      };
347      return SelectionSharer;
348    });
349
350  }
351  else {
352    // Registering SelectionSharer as a global
353    // Usage: var sharer = new SelectionSharer('p');
354    window.SelectionSharer = SelectionSharer;
355  }
356
357})(jQuery);
358
359

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.