File: cloud.js

package info (click to toggle)
pygresql 1%3A5.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 3,340 kB
  • ctags: 2,187
  • sloc: python: 13,239; ansic: 4,975; makefile: 164
file content (658 lines) | stat: -rw-r--r-- 24,764 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
/* ~~~~~~~~~~~~~~
 * cloud.js_t
 * ~~~~~~~~~~~~~~
 *
 * Various bits of javascript driving the moving parts behind various
 * parts of the cloud theme. Handles things such as toggleable sections,
 * collapsing the sidebar, etc.
 *
 * :copyright: Copyright 2011-2012 by Assurance Technologies
 * :license: BSD
 */



  
  


// begin encapsulation
(function (window, $, _){

/* ==========================================================================
 * common helpers
 * ==========================================================================*/
var isUndef = _.isUndefined,
    TEXT_NODE = 3; // could use Node.TEXT_NODE, but IE doesn't define it :(

// helper to generate an absolute url path from a relative one.
// absolute paths passed through unchanged.
// paths treated as relative to <base>,
// if base is omitted, uses directory of current document.
function abspath(path, base) {
    var parts = path.split("/"),
        stack = [];
    if (parts[0]) {
      // if path is relative, put base on stack
      stack = (base || document.location.pathname).split("/");
      // remove blank from leading '/'
      if (!stack[0]) { stack.shift(); }
      // discard filename & blank from trailing '/'
      if (stack.length && !(base && stack[stack.length-1])) { stack.pop(); }
    }
    for (var i=0; i < parts.length; ++i) {
      if (parts[i] && parts[i] != '.') {
        if (parts[i] == '..'){
          stack.pop();
        } else {
          stack.push(parts[i]);
        }
      }
    }
    return "/" + stack.join("/");
}

// helper to normalize urls for comparison
// * strips current document's scheme, host, & path from local document links (just fragment will be left)
// * strips current document's scheme & host from internal urls (just path + fragment will be left)
// * makes all internal url paths absolute
// * external urls returned unchanged.
var // hosturl is url to root of host, without trailing '/'
    // allowing netloc segment to be empty to support 'file:///' urls
    hosturl = document.location.href.match(/^[a-z0-9]+:(?:\/\/)?(?:[^@]*@)?[^/]*/)[0],
    docpath = document.location.pathname;
function shorten_url(url) {
  if (url.indexOf(hosturl) == 0) {
    url = url.substr(hosturl.length) || '/';
  } else if (url && url[0] == '.') {
    url = abspath(url);
  }
  if (url.indexOf(docpath) == 0) {
    url = url.substr(docpath.length);
  }
  if (url == "#") { url = ""; } // border case from sphinxlocaltoc
  return url;
}

// helper that retrieves css property in pixels
function csspx(elem, prop) {
  return 1 * $(elem).css(prop).replace(/px$/, '');
}

/* debugging
window.CloudSphinxTheme = {
  hosturl: hosturl,
  docpath: docpath,
  shorten_url: shorten_url,
};
*/

// NOTE: would use $().offset(), but it's document-relative,
//       and we need viewport-relative... which means getBoundingClientRect().
function leftViewOffset($node){ return ($node && $node.length > 0) ? $node[0].getBoundingClientRect().left : 0; }
function topViewOffset($node){ return ($node && $node.length > 0) ? $node[0].getBoundingClientRect().top : 0; }

// return normalized nodename, takes in node or jquery selector
// (can't trust nodeName, per http://ejohn.org/blog/nodename-case-sensitivity/)
function nodeName(elem) {
  if (elem && elem.length) { elem = elem[0]; }
  return elem && elem.nodeName.toUpperCase();
}

/* ==========================================================================
 * highlighter #2
 * ==========================================================================
 *
 * Sphinx's highlighter marks some objects when user follows link,
 * but doesn't include section names, etc. This catches those.
 */
$(document).ready(function (){
  // helper to locate highlight target based on #fragment
  function locate_target(){
    // find id referenced by #fragment
    var hash = document.location.hash;
    if(!hash) return null;
    var section = document.getElementById(hash.substr(1));
    if(!section) return null;

    // could be div.section, or hidden span at top of div.section
    var name = nodeName(section);
    if(name != "DIV"){
      if(name == "SPAN" && section.innerHTML == "" &&
         nodeName(section.parentNode) == "DIV")
      {
          section = section.parentNode;
      }
      else if (name == "DT" && section.children.length &&
               $(section).children("tt.descname, code.descname").length > 0)
      {
        // not a section, but an object definition, e.g. a class, func, or attr
        return $(section);
      }
    }
    // now at section div and either way we have to find title element - h2, h3, etc.
    var header = $(section).children("h2, h3, h4, h5, h6").first();
    return header.length ? header : null;
  }

  // init highlight
  var target = locate_target();
  if(target) target.addClass("highlighted");

  // update highlight if hash changes
  $(window).bind("hashchange", function () {
    if(target) target.removeClass("highlighted");
    target = locate_target();
    if(target) target.addClass("highlighted");
  });
});

/* ==========================================================================
 * toggleable sections
 * ==========================================================================
 *
 * Added expand/collapse button to any collapsible RST sections.
 * Looks for sections with CSS class "html-toggle",
 * along with the optional classes "expanded" or "collapsed".
 * Button toggles "html-toggle.expanded/collapsed" classes,
 * and relies on CSS to do the rest of the job displaying them as appropriate.
 */

$(document).ready(function (){
  function init(){
    // get header & section, and add static classes
    var header = $(this);
    var section = header.parent();
    header.addClass("html-toggle-button");

    // helper to test if url hash is within this section
    function contains_hash(){
      var hash = document.location.hash;
      return hash && (section[0].id == hash.substr(1) ||
              section.find(hash.replace(/\./g,"\\.")).length>0);
    }

    // helper to control toggle state
    function set_state(expanded){
      expanded = !!expanded; // toggleClass et al need actual boolean
      section.toggleClass("expanded", expanded);
      section.toggleClass("collapsed", !expanded);
      section.children().toggle(expanded);
      if (!expanded) {
        section.children("span:first-child:empty").show(); /* for :ref: span tag */
        header.show();
      }
    }

    // initialize state
    set_state(section.hasClass("expanded") || contains_hash());

    // bind toggle callback
    header.click(function (evt){
      var state = section.hasClass("expanded")
      if(state && $(evt.target).is(".headerlink")) { return; }
      set_state(!state);
      $(window).trigger('cloud-section-toggled', section[0]);
    });

    // open section if user jumps to it from w/in page
    $(window).bind("hashchange", function () {
      if(contains_hash()) set_state(true);
    });
  }

  $(".html-toggle.section > h2, .html-toggle.section > h3, .html-toggle.section > h4, .html-toggle.section > h5, .html-toggle.section > h6").each(init);
});
/* ==========================================================================
 * collapsible sidebar
 * ==========================================================================
 *
 * Adds button for collapsing & expanding sidebar,
 * which toggles "document.collapsed-sidebar" CSS class,
 * and relies on CSS for actual styling of visible & hidden sidebars.
 */

$(document).ready(function (){
  if(!$('.sphinxsidebar').length){
    return;
  }
  
    var close_arrow = '«';
    var open_arrow = 'sidebar »';
  
  var holder = $('<div class="sidebartoggle"><button id="sidebar-hide" title="click to hide the sidebar">' +
                 close_arrow + '</button><button id="sidebar-show" style="display: none" title="click to show the sidebar">' +
                 open_arrow + '</button></div>');
  var doc = $('div.document');

  var show_btn = $('#sidebar-show', holder);
  var hide_btn = $('#sidebar-hide', holder);
  var copts = { expires: 7, path: abspath(DOCUMENTATION_OPTIONS.URL_ROOT || "") };

  show_btn.click(function (){
    doc.removeClass("collapsed-sidebar");
    hide_btn.show();
    show_btn.hide();
    $.cookie("sidebar", "expanded", copts);
    $(window).trigger("cloud-sidebar-toggled", false);
  });

  hide_btn.click(function (){
    doc.addClass("collapsed-sidebar");
    show_btn.show();
    hide_btn.hide();
    $.cookie("sidebar", "collapsed", copts);
    $(window).trigger("cloud-sidebar-toggled", true);
  });

  var state = $.cookie("sidebar");

  if(!state)
    state = "collapsed";


  doc.append(holder);

  if (state == "collapsed"){
    doc.addClass("collapsed-sidebar");
    show_btn.show();
    hide_btn.hide();
  }
});
/* ==========================================================================
 * sticky sidebar
 * ==========================================================================
 *
 * Instrument sidebar so that it sticks in place as page is scrolled.
 */
$(document).ready(function (){
  // initialize references to relevant elements
  var holder = $('.document'); // element that sidebar sits within
  var sidebar = $('.sphinxsidebar'); // element we're making "sticky"
  var toc_header = $('.sphinxlocaltoc h3'); // toc header + list control position
      if(!toc_header.length) toc_header = null;
  var toc_list = toc_header ? toc_header.next("ul") : null;
  var toggle = $('.sidebartoggle'); // also make collapse button sticky

  // initialize internal state
  var sticky_disabled = false, // whether sticky is disabled for given window size
      sidebar_adjust = 0; // vertical offset within sidebar when sticky

  // function to set style for given state
  function set_style(target, value, adjust)
  {
    if(value <= adjust || sticky_disabled){
      target.css({marginLeft: "", position: "", top: "", left: "", bottom: ""});
    }
    else if (value <= holder.height() - target.height() + adjust){
      target.css({marginLeft: 0, position: "fixed", top: -adjust,
                 left: leftViewOffset(holder), bottom: ""});
    }
    else{
      target.css({marginLeft: 0, position: "absolute", top: "", left: 0, bottom: 0});
    }
  }

  // func to update sidebar position based on scrollbar & container positions
  function update_sticky(){
    // set sidebar position
    var offset = -topViewOffset(holder);
    set_style(sidebar, offset, sidebar_adjust);
    // collapse button should follow along as well
    set_style(toggle, offset, 0);
  };

  // func to update sidebar measurements, and then call update_sticky()
  function update_measurements(){
    sticky_disabled = false;
    sidebar_adjust = 0;
    if(toc_header){
      // check how much room we have to display top of sidebar -> end of toc list
      var leftover = $(window).height() - (toc_list.height() + topViewOffset(toc_list) - topViewOffset(sidebar));
      if(leftover < 0){
        // not enough room if we align top of sidebar to window,
        // try aligning to top of toc list instead
        sidebar_adjust = topViewOffset(toc_header) - topViewOffset(sidebar) - 8;
        if(leftover + sidebar_adjust < 0){
          // still not enough room - disable sticky sidebar
          sticky_disabled = true;
        }
      }
    }
    update_sticky();
  }

  // run function now, and every time window scrolls
  update_measurements();
  $(window).scroll(update_sticky)
           .resize(update_measurements)
           .bind('hashchange', update_measurements)
           .bind('cloud-section-toggled', update_measurements);
});


/* ==========================================================================
 * sidebar toc highlighter
 * ==========================================================================
 *
 * highlights toc entry for current section being viewed.
 */
$(document).ready(function (){
  // scan & init all links w/in local & global toc
  var $links = $(".sphinxlocaltoc ul a, .sphinxglobaltoc li.current a");
  $links = $links.filter(function () {
    // grab basic info about link
    var $link = $(this),
        href = shorten_url($link.prop("href")),
        $item = $link.parent("li"),
        $parent = $item.parent("ul").prev("a"),
        parent_entry = $parent.data("toc"),
        $target;

    // handle the various types of links
    if (!href || href[0] == "#") {
      // link points to section in current document.
      // use that section as visibility target
      $target = href ? $(href).first() : $("h1").parent();
      $link.addClass("local");

    } else if (!parent_entry) {
      // parent link isn't local, so we can ignore this link
      return false;

    } else {
      // parent is part of page, so this is link for child page.
      // prefer to use actual link in document as visibility target
      $target = parent_entry.target.find("a").filter(function (){ return shorten_url(this.href) == href; });
      if (!$target.length) {
        // fall back to parent section
        $target = parent_entry.target;
      } else if ($target.parent("li").attr("class").search(/(^|\w)toctree-/) == 0) {
        // it's part of embedded toc tree, use whole item.
        $target = $target.parent("li");
      }
      $link.addClass("child");
    }

    // add entry for link containing pre-cached ref to all the data highlighter needs.
    var $ul = $link.next("ul");
    if (!href || !$ul.length || $ul.find("a").length < 4) { $ul = null; }
    else { $item.addClass("toc-toggle"); }
    $link.data("toc", {target: $target, // section/object controlling link's visibility
                       item: $item, // list item where we set state flags
                       child_ul: $ul, // list of child items if we're doing collapse checking, else null
                       parent: $parent.length ? $parent : null, // parent link, if any, else null
                       parent_entry: parent_entry, // direct reference to parent entry
                       first_child_target: null, // first child $target, if any
                       active_child: null // first active child link, if any -- updated each rebuild
                       });

    // set first_child on parent entry, if needed.
    if (parent_entry && !parent_entry.first_child_target && !$link.hasClass("child")) {
      parent_entry.first_child_target = $target;
    }
    return true;
  });

  // if all links got filtered, nothing to do.
  if(!$links.length) return;

  // debugging helper
//  var $cutoff = $('<div/>', {id: "cutoff", style:'position: fixed; left: 0; right: 0; background: rgba(255,0,0,0.1); '}).appendTo("body");

  // function to update toc markers
  function update_visible_sections(evt, first_run){
    // determine viewable range -- make this call once for speed
    var height = $(window).height(),
        line_height = csspx(".body", "line-height"),
        topCutoff = Math.floor(Math.min(height * 0.15, 5 * line_height));
//        bottomCutoff = height - Math.floor(Math.min(height * 0.5, 30 * line_height));
//    $cutoff.css({height: topCutoff, top: 0});
//    $cutoff.css({height: height - bottomCutoff, bottom: 0});

    // update flags for all link elements.
    for (var i=0; i < $links.length; ++i) {
      // grab link element & work out state
      var $link = $($links[i]),
          entry = $link.data("toc"),
          parent_entry = entry.parent_entry,
          $target = entry.target,
          $li = entry.item,
            // viewport-relative position of target
          rect = $target[0].getBoundingClientRect(), // NOTE: width/height not available in IE8-
            // whether section contents are visible
          target_hidden = (rect.bottom < 0 || rect.top > height),
          hidden = target_hidden || $target.hasClass("collapsed"),
            // whether this is the 'active' section
          active = (!hidden && (!parent_entry ||
                                (parent_entry.item.hasClass("active") &&
                                !parent_entry.active_child &&
                                topViewOffset(parent_entry.first_child_target) < topCutoff)) &&
                    rect.bottom >= topCutoff && !$link.hasClass("child"));

      // update active status
      entry.active_child = null;
      if (parent_entry && active) {
        parent_entry.active_child = $link;
        parent_entry.item.removeClass("final");
      }
      $li.toggleClass("active", active);
      $li.toggleClass("final", active);

// NOTE: works, but 'visible' flag not used
/*      // update visible status -- unlike active, excludes child content.
      // NOTE: using 'target_hidden' so collapsed sections still show up
      $li.toggleClass("visible", !target_hidden &&
                      topViewOffset(entry.first_child_target) >= 0); */

      // update collapsed status of child entries
      var $ul = entry.child_ul;
      if ($ul){
        // XXX: this logic open sections when they're visible,
        //      but creates a lot of motion in the TOC.
//        var collapsed = hidden || (!active && (rect.bottom < topCutoff || rect.top > bottomCutoff));
        var collapsed = hidden || !active;
        entry.just_opened = false;
        if($li.hasClass("collapsed") != collapsed) {
          if(!collapsed) {
            if(parent_entry && parent_entry.just_opened) {
              $ul.show(); // don't animate if parent is animated.
            }else {
              $ul.slideDown();
            }
            entry.just_opened = true;
          } else if (first_run) {
            $ul.hide(); // don't animate things on first run
          } else {
            $ul.slideUp();
          }
          $li.toggleClass("collapsed", collapsed);
        }
      }
    }
  }

  // run function now, and every time window is resized
  // TODO: disable when sidebar isn't sticky (including when window is too small)
  //       and when sidebar is collapsed / invisible
  update_visible_sections(null, true);
  $(window).scroll(update_visible_sections)
           .resize(update_visible_sections)
           .bind('hashchange', update_visible_sections)
           .bind('cloud-section-toggled', update_visible_sections)
           .bind('cloud-sidebar-toggled', update_visible_sections);
});


/* ==========================================================================
 * header breaker
 * ==========================================================================
 *
 * attempts to intelligently insert linebreaks into page titles, where possible.
 * currently only handles titles such as "module - description",
 * adding a break after the "-".
 */
$(document).ready(function (){
  // get header's content, insert linebreaks
  var header = $("h1");
  var orig = header[0].innerHTML;
  var shorter = orig;
  if($("h1 > a:first > tt > span.pre").length > 0){
      shorter = orig.replace(/(<\/tt><\/a>\s*[-\u2013\u2014:]\s+)/im, "$1<br> ");
  }
  else if($("h1 > tt.literal:first").length > 0){
      shorter = orig.replace(/(<\/tt>\s*[-\u2013\u2014:]\s+)/im, "$1<br> ");
  }
  if(shorter == orig){
    return;
  }

  // hack to determine full width of header
  header.css({whiteSpace: "nowrap", position:"absolute"});
  var header_width = header.width();
  header.css({whiteSpace: "", position: ""});

  // func to insert linebreaks when needed
  function layout_header(){
    header[0].innerHTML = (header_width > header.parent().width()) ? shorter : orig;
  }

  // run function now, and every time window is resized
  layout_header();
  $(window).resize(layout_header)
           .bind('cloud-sidebar-toggled', layout_header);
});


/* ==========================================================================
 * toc cleaner
 * ==========================================================================
 *
 * attempts to remove clutter from toc lists.
 * mainly, looks for toc entries with format "{module} -- {desc}"",
 * and reduces them down to just "" to save space.
 */
$(document).ready(function (){
  var $toc = $(".sphinxglobaltoc"),
      candidates = {};

  // scan TOC for module entries
  $toc.find("a.internal.reference:has(.literal:first-child > .pre)").each(function (){
    var $this = $(this),
        text = $this.find(".literal").text();

        // wrap details in .objdesc class
        $this.contents().filter(function (){
          return ((this.nodeType == TEXT_NODE))
        }).wrap('<span class="objdesc"></span>');

        // work out modules that have exactly 1 toc entry
        if (isUndef(candidates[text])) {
          candidates[text] = $this;
        }else{
          candidates[text] = null;
        }
  });

  // for all modules that had unique names, add 'unique' flag
  $.each(candidates, function (text, $entry){
    if ($entry) { $entry.children(".objdesc").addClass("unique"); }
  });

});



/* ==========================================================================
 * auto determine when admonition should have inline / block title
 * under this mode, the css will default to styling everything like a block,
 * so we just mark everything that shouldn't be blocked out.
 * ==========================================================================
 */
$(document).ready(function (){
  $("div.body div.admonition:not(.inline-title):not(.block-title)" +
                           ":not(.danger):not(.error)" +
                           ":has(p.first + p.last)").addClass("inline-title");
});


/* ==========================================================================
 * codeblock lineno aligner
 * if document contains multiple codeblocks, and some have different counts
 * (e.g. 10 lines vs 300 lines), the alignment will look off, since the
 * 300 line block will be indented 1 extra space to account for the hundreds.
 * this unifies the widths of all such blocks (issue 19)
 * ==========================================================================
 */
$(document).ready(function (){
  var $lines = $(".linenodiv pre");
  if(!$lines.length) { return; }
  // NOTE: using ems so this holds under font size changes
  var largest = Math.max.apply(null, $lines.map(function () { return $(this).innerWidth(); })),
      em_to_px = csspx($lines, "font-size");
  $lines.css("width", (largest / em_to_px) + "em").css("text-align", "right");
});

/* ==========================================================================
 * codeblock copy helper button
 * ==========================================================================
 *
 * Add a [>>>] button on the top-right corner of code samples to hide
 * the '>>>' and '...' prompts and the output and thus make the code
 * copyable. Also hides linenumbers.
 *
 * Adapted from copybutton.js,
 * Copyright 2014 PSF. Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
 * File originates from the cpython source found in Doc/tools/sphinxext/static/copybutton.js
 *
 */
$(document).ready(function() {
  // TODO: enhance this to hide linenos for ALL highlighted code blocks,
  //       and only perform python-specific hiding as-needed.

  // static text
  var hide_text = 'Hide the prompts and output',
      show_text = 'Show the prompts and output';

  // helper which sets button & codeblock state
  function setButtonState($button, active) {
    $button.parent().find('.go, .gp, .gt').toggle(!active);
    $button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', active ? 'hidden' : 'visible');
    $button.closest(".highlighttable").find(".linenos pre").css('visibility', active ? 'hidden' : 'visible');
    $button.attr('title', active ? show_text : hide_text);
    $button.toggleClass("active", active);
  }

  // create and add the button to all the code blocks containing a python prompt
  var $blocks = $('.highlight-python, .highlight-python3');
  $blocks.find(".highlight:has(pre .gp)").each(function() {
    var $block = $(this);

    // tracebacks (.gt) contain bare text elements that need to be
    // wrapped in a span to work with .nextUntil() call in setButtonState()
    $block.find('pre:has(.gt)').contents().filter(function() {
      return ((this.nodeType == TEXT_NODE) && (this.data.trim().length > 0));
    }).wrap('<span>');

    // insert button into block
    var $button = $('<button class="copybutton">&gt;&gt;&gt;</button>');
    $block.css("position", "relative").prepend($button);
    setButtonState($button, false);
  });

  // toggle button state when clicked
  $('.copybutton').click(
    function() {
      var $button = $(this);
      setButtonState($button, !$button.hasClass("active"));
    });
});

/* ==========================================================================
 * eof
 * ==========================================================================
 */

// end encapsulation
// NOTE: sphinx provides underscore.js as $u
}(window, jQuery, $u));