text:
StyleSheetList.prototype.reload_interval = 1000; // 1 second

CSSStyleSheet.prototype.reload = function reload(){
  // Reload one stylesheet
  // usage: document.styleSheets[0].reload()
  // return: URI of stylesheet if it could be reloaded, overwise undefined
  if (this.href) {
    var href = this.href;
    var i = href.indexOf('?'),
        last_reload = 'last_reload=' + (new Date).getTime();
    if (i < 0) {
      href += '?' + last_reload;
    } else if (href.indexOf('last_reload=', i) < 0) {
      href += '&' + last_reload;
    } else {
      href = href.replace(/last_reload=\d+/, last_reload);
    }
    return this.ownerNode.href = href;
  }
};

StyleSheetList.prototype.reload = function reload(){
  // Reload all stylesheets
  // usage: document.styleSheets.reload()
  for (var i=0; i<this.length; i++) {
    this[i].reload()
  }
};

StyleSheetList.prototype.start_autoreload = function start_autoreload(miliseconds /*Number*/){
  // usage: document.styleSheets.start_autoreload()
  if (!start_autoreload.running) {
    var styles = this;
    start_autoreload.running = setInterval(function reloading(){
      styles.reload();
    }, miliseconds || this.reload_interval);
  }
  return start_autoreload.running;
};

StyleSheetList.prototype.stop_autoreload = function stop_autoreload(){
  // usage: document.styleSheets.stop_autoreload()
  clearInterval(this.start_autoreload.running);
  this.start_autoreload.running = null;
};

StyleSheetList.prototype.toggle_autoreload = function toggle_autoreload(){
  // usage: document.styleSheets.toggle_autoreload()
  return this.start_autoreload.running ? this.stop_autoreload() : this.start_autoreload();
};

repo_name: cdglabs/Shadershop

repo_language: JavaScript

mime_type: text/plain


text:
//     keymaster.js
//     (c) 2011-2013 Thomas Fuchs
//     keymaster.js may be freely distributed under the MIT license.

;(function(global){
  var k,
    _handlers = {},
    _mods = { 16: false, 18: false, 17: false, 91: false },
    _scope = 'all',
    // modifier keys
    _MODIFIERS = {
      '⇧': 16, shift: 16,
      '⌥': 18, alt: 18, option: 18,
      '⌃': 17, ctrl: 17, control: 17,
      '⌘': 91, command: 91
    },
    // special keys
    _MAP = {
      backspace: 8, tab: 9, clear: 12,
      enter: 13, 'return': 13,
      esc: 27, escape: 27, space: 32,
      left: 37, up: 38,
      right: 39, down: 40,
      del: 46, 'delete': 46,
      home: 36, end: 35,
      pageup: 33, pagedown: 34,
      ',': 188, '.': 190, '/': 191,
      '`': 192, '-': 189, '=': 187,
      ';': 186, '\'': 222,
      '[': 219, ']': 221, '\\': 220
    },
    code = function(x){
      return _MAP[x] || x.toUpperCase().charCodeAt(0);
    },
    _downKeys = [];

  for(k=1;k<20;k++) _MAP['f'+k] = 111+k;

  // IE doesn't support Array#indexOf, so have a simple replacement
  function index(array, item){
    var i = array.length;
    while(i--) if(array[i]===item) return i;
    return -1;
  }

  // for comparing mods before unassignment
  function compareArray(a1, a2) {
    if (a1.length != a2.length) return false;
    for (var i = 0; i < a1.length; i++) {
        if (a1[i] !== a2[i]) return false;
    }
    return true;
  }

  var modifierMap = {
      16:'shiftKey',
      18:'altKey',
      17:'ctrlKey',
      91:'metaKey'
  };
  function updateModifierKey(event) {
      for(k in _mods) _mods[k] = event[modifierMap[k]];
  };

  // handle keydown event
  function dispatch(event) {
    var key, handler, k, i, modifiersMatch, scope;
    key = event.keyCode;

    if (index(_downKeys, key) == -1) {
        _downKeys.push(key);
    }

    // if a modifier key, set the key.<modifierkeyname> property to true and return
    if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
    if(key in _mods) {
      _mods[key] = true;
      // 'assignKey' from inside this closure is exported to window.key
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
      return;
    }
    updateModifierKey(event);

    // see if we need to ignore the keypress (filter() can can be overridden)
    // by default ignore key presses if a select, textarea, or input is focused
    if(!assignKey.filter.call(this, event)) return;

    // abort if no potentially matching shortcuts found
    if (!(key in _handlers)) return;

    scope = getScope();

    // for each potential shortcut
    for (i = 0; i < _handlers[key].length; i++) {
      handler = _handlers[key][i];

      // see if it's in the current scope
      if(handler.scope == scope || handler.scope == 'all'){
        // check if modifiers match if any
        modifiersMatch = handler.mods.length > 0;
        for(k in _mods)
          if((!_mods[k] && index(handler.mods, +k) > -1) ||
            (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
        // call the handler and stop the event if neccessary
        if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
          if(handler.method(event, handler)===false){
            if(event.preventDefault) event.preventDefault();
              else event.returnValue = false;
            if(event.stopPropagation) event.stopPropagation();
            if(event.cancelBubble) event.cancelBubble = true;
          }
        }
      }
    }
  };

  // unset modifier keys on keyup
  function clearModifier(event){
    var key = event.keyCode, k,
        i = index(_downKeys, key);

    // remove key from _downKeys
    if (i >= 0) {
        _downKeys.splice(i, 1);
    }

    if(key == 93 || key == 224) key = 91;
    if(key in _mods) {
      _mods[key] = false;
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
    }
  };

  function resetModifiers() {
    for(k in _mods) _mods[k] = false;
    for(k in _MODIFIERS) assignKey[k] = false;
  };

  // parse and assign shortcut
  function assignKey(key, scope, method){
    var keys, mods;
    keys = getKeys(key);
    if (method === undefined) {
      method = scope;
      scope = 'all';
    }

    // for each shortcut
    for (var i = 0; i < keys.length; i++) {
      // set modifier keys if any
      mods = [];
      key = keys[i].split('+');
      if (key.length > 1){
        mods = getMods(key);
        key = [key[key.length-1]];
      }
      // convert to keycode and...
      key = key[0]
      key = code(key);
      // ...store handler
      if (!(key in _handlers)) _handlers[key] = [];
      _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
    }
  };

  // unbind all handlers for given key in current scope
  function unbindKey(key, scope) {
    var multipleKeys, keys,
      mods = [],
      i, j, obj;

    multipleKeys = getKeys(key);

    for (j = 0; j < multipleKeys.length; j++) {
      keys = multipleKeys[j].split('+');

      if (keys.length > 1) {
        mods = getMods(keys);
        key = keys[keys.length - 1];
      }

      key = code(key);

      if (scope === undefined) {
        scope = getScope();
      }
      if (!_handlers[key]) {
        return;
      }
      for (i in _handlers[key]) {
        obj = _handlers[key][i];
        // only clear handlers if correct scope and mods match
        if (obj.scope === scope && compareArray(obj.mods, mods)) {
          _handlers[key][i] = {};
        }
      }
    }
  };

  // Returns true if the key with code 'keyCode' is currently down
  // Converts strings into key codes.
  function isPressed(keyCode) {
      if (typeof(keyCode)=='string') {
        keyCode = code(keyCode);
      }
      return index(_downKeys, keyCode) != -1;
  }

  function getPressedKeyCodes() {
      return _downKeys.slice(0);
  }

  function filter(event){
    var tagName = (event.target || event.srcElement).tagName;
    // ignore keypressed in any elements that support keyboard data input
    return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
  }

  // initialize key.<modifier> to false
  for(k in _MODIFIERS) assignKey[k] = false;

  // set current scope (default 'all')
  function setScope(scope){ _scope = scope || 'all' };
  function getScope(){ return _scope || 'all' };

  // delete all handlers for a given scope
  function deleteScope(scope){
    var key, handlers, i;

    for (key in _handlers) {
      handlers = _handlers[key];
      for (i = 0; i < handlers.length; ) {
        if (handlers[i].scope === scope) handlers.splice(i, 1);
        else i++;
      }
    }
  };

  // abstract key logic for assign and unassign
  function getKeys(key) {
    var keys;
    key = key.replace(/\s/g, '');
    keys = key.split(',');
    if ((keys[keys.length - 1]) == '') {
      keys[keys.length - 2] += ',';
    }
    return keys;
  }

  // abstract mods logic for assign and unassign
  function getMods(key) {
    var mods = key.slice(0, key.length - 1);
    for (var mi = 0; mi < mods.length; mi++)
    mods[mi] = _MODIFIERS[mods[mi]];
    return mods;
  }

  // cross-browser events
  function addEvent(object, event, method) {
    if (object.addEventListener)
      object.addEventListener(event, method, false);
    else if(object.attachEvent)
      object.attachEvent('on'+event, function(){ method(window.event) });
  };

  // set the handlers globally on document
  addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
  addEvent(document, 'keyup', clearModifier);

  // reset modifiers to false whenever the window is (re)focused.
  addEvent(window, 'focus', resetModifiers);

  // store previously defined key
  var previousKey = global.key;

  // restore previously defined key and return reference to our key object
  function noConflict() {
    var k = global.key;
    global.key = previousKey;
    return k;
  }

  // set window.key and window.key.set/get/deleteScope, and the default filter
  global.key = assignKey;
  global.key.setScope = setScope;
  global.key.getScope = getScope;
  global.key.deleteScope = deleteScope;
  global.key.filter = filter;
  global.key.isPressed = isPressed;
  global.key.getPressedKeyCodes = getPressedKeyCodes;
  global.key.noConflict = noConflict;
  global.key.unbind = unbindKey;

  if(typeof module !== 'undefined') module.exports = key;

})(this);


text:
/**
 * StyleFix 1.0.3 & PrefixFree 1.0.7
 * @author Lea Verou
 * MIT license
 */

(function(){

if(!window.addEventListener) {
  return;
}

var self = window.StyleFix = {
  link: function(link) {
    try {
      // Ignore stylesheets with data-noprefix attribute as well as alternate stylesheets
      if(link.rel !== 'stylesheet' || link.hasAttribute('data-noprefix')) {
        return;
      }
    }
    catch(e) {
      return;
    }

    var url = link.href || link.getAttribute('data-href'),
        base = url.replace(/[^\/]+$/, ''),
        base_scheme = (/^[a-z]{3,10}:/.exec(base) || [''])[0],
        base_domain = (/^[a-z]{3,10}:\/\/[^\/]+/.exec(base) || [''])[0],
        base_query = /^([^?]*)\??/.exec(url)[1],
        parent = link.parentNode,
        xhr = new XMLHttpRequest(),
        process;

    xhr.onreadystatechange = function() {
      if(xhr.readyState === 4) {
        process();
      }
    };

    process = function() {
        var css = xhr.responseText;

        if(css && link.parentNode && (!xhr.status || xhr.status < 400 || xhr.status > 600)) {
          css = self.fix(css, true, link);

          // Convert relative URLs to absolute, if needed
          if(base) {
            css = css.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi, function($0, quote, url) {
              if(/^([a-z]{3,10}:|#)/i.test(url)) { // Absolute & or hash-relative
                return $0;
              }
              else if(/^\/\//.test(url)) { // Scheme-relative
                // May contain sequences like /../ and /./ but those DO work
                return 'url("' + base_scheme + url + '")';
              }
              else if(/^\//.test(url)) { // Domain-relative
                return 'url("' + base_domain + url + '")';
              }
              else if(/^\?/.test(url)) { // Query-relative
                return 'url("' + base_query + url + '")';
              }
              else {
                // Path-relative
                return 'url("' + base + url + '")';
              }
            });

            // behavior URLs shoudn’t be converted (Issue #19)
            // base should be escaped before added to RegExp (Issue #81)
            var escaped_base = base.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1");
            css = css.replace(RegExp('\\b(behavior:\\s*?url\\(\'?"?)' + escaped_base, 'gi'), '$1');
            }

          var style = document.createElement('style');
          style.textContent = css;
          style.media = link.media;
          style.disabled = link.disabled;
          style.setAttribute('data-href', link.getAttribute('href'));

          parent.insertBefore(style, link);
          parent.removeChild(link);

          style.media = link.media; // Duplicate is intentional. See issue #31
        }
    };

    try {
      xhr.open('GET', url);
      xhr.send(null);
    } catch (e) {
      // Fallback to XDomainRequest if available
      if (typeof XDomainRequest != "undefined") {
        xhr = new XDomainRequest();
        xhr.onerror = xhr.onprogress = function() {};
        xhr.onload = process;
        xhr.open("GET", url);
        xhr.send(null);
      }
    }

    link.setAttribute('data-inprogress', '');
  },

  styleElement: function(style) {
    if (style.hasAttribute('data-noprefix')) {
      return;
    }
    var disabled = style.disabled;

    style.textContent = self.fix(style.textContent, true, style);

    style.disabled = disabled;
  },

  styleAttribute: function(element) {
    var css = element.getAttribute('style');

    css = self.fix(css, false, element);

    element.setAttribute('style', css);
  },

  process: function() {
    // Linked stylesheets
    $('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);

    // Inline stylesheets
    $('style').forEach(StyleFix.styleElement);

    // Inline styles
    $('[style]').forEach(StyleFix.styleAttribute);
  },

  register: function(fixer, index) {
    (self.fixers = self.fixers || [])
      .splice(index === undefined? self.fixers.length : index, 0, fixer);
  },

  fix: function(css, raw, element) {
    for(var i=0; i<self.fixers.length; i++) {
      css = self.fixers[i](css, raw, element) || css;
    }

    return css;
  },

  camelCase: function(str) {
    return str.replace(/-([a-z])/g, function($0, $1) { return $1.toUpperCase(); }).replace('-','');
  },

  deCamelCase: function(str) {
    return str.replace(/[A-Z]/g, function($0) { return '-' + $0.toLowerCase() });
  }
};

/**************************************
 * Process styles
 **************************************/
(function(){
  setTimeout(function(){
    $('link[rel="stylesheet"]').forEach(StyleFix.link);
  }, 10);

  document.addEventListener('DOMContentLoaded', StyleFix.process, false);
})();

function $(expr, con) {
  return [].slice.call((con || document).querySelectorAll(expr));
}

})();

/**
 * PrefixFree
 */
(function(root){

if(!window.StyleFix || !window.getComputedStyle) {
  return;
}

// Private helper
function fix(what, before, after, replacement, css) {
  what = self[what];

  if(what.length) {
    var regex = RegExp(before + '(' + what.join('|') + ')' + after, 'gi');

    css = css.replace(regex, replacement);
  }

  return css;
}

var self = window.PrefixFree = {
  prefixCSS: function(css, raw, element) {
    var prefix = self.prefix;

    // Gradient angles hotfix
    if(self.functions.indexOf('linear-gradient') > -1) {
      // Gradients are supported with a prefix, convert angles to legacy
      css = css.replace(/(\s|:|,)(repeating-)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig, function ($0, delim, repeating, deg) {
        return delim + (repeating || '') + 'linear-gradient(' + (90-deg) + 'deg';
      });
    }

    css = fix('functions', '(\\s|:|,)', '\\s*\\(', '$1' + prefix + '$2(', css);
    css = fix('keywords', '(\\s|:)', '(\\s|;|\\}|$)', '$1' + prefix + '$2$3', css);
    css = fix('properties', '(^|\\{|\\s|;)', '\\s*:', '$1' + prefix + '$2:', css);

    // Prefix properties *inside* values (issue #8)
    if (self.properties.length) {
      var regex = RegExp('\\b(' + self.properties.join('|') + ')(?!:)', 'gi');

      css = fix('valueProperties', '\\b', ':(.+?);', function($0) {
        return $0.replace(regex, prefix + "$1")
      }, css);
    }

    if(raw) {
      css = fix('selectors', '', '\\b', self.prefixSelector, css);
      css = fix('atrules', '@', '\\b', '@' + prefix + '$1', css);
    }

    // Fix double prefixing
    css = css.replace(RegExp('-' + prefix, 'g'), '-');

    // Prefix wildcard
    css = css.replace(/-\*-(?=[a-z]+)/gi, self.prefix);

    return css;
  },

  property: function(property) {
    return (self.properties.indexOf(property) >=0 ? self.prefix : '') + property;
  },

  value: function(value, property) {
    value = fix('functions', '(^|\\s|,)', '\\s*\\(', '$1' + self.prefix + '$2(', value);
    value = fix('keywords', '(^|\\s)', '(\\s|$)', '$1' + self.prefix + '$2$3', value);

    if(self.valueProperties.indexOf(property) >= 0) {
      value = fix('properties', '(^|\\s|,)', '($|\\s|,)', '$1'+self.prefix+'$2$3', value);
    }

    return value;
  },

  // Warning: Prefixes no matter what, even if the selector is supported prefix-less
  prefixSelector: function(selector) {
    return selector.replace(/^:{1,2}/, function($0) { return $0 + self.prefix })
  },

  // Warning: Prefixes no matter what, even if the property is supported prefix-less
  prefixProperty: function(property, camelCase) {
    var prefixed = self.prefix + property;

    return camelCase? StyleFix.camelCase(prefixed) : prefixed;
  }
};

/**************************************
 * Properties
 **************************************/
(function() {
  var prefixes = {},
    properties = [],
    shorthands = {},
    style = getComputedStyle(document.documentElement, null),
    dummy = document.createElement('div').style;

  // Why are we doing this instead of iterating over properties in a .style object? Cause Webkit won't iterate over those.
  var iterate = function(property) {
    if(property.charAt(0) === '-') {
      properties.push(property);

      var parts = property.split('-'),
        prefix = parts[1];

      // Count prefix uses
      prefixes[prefix] = ++prefixes[prefix] || 1;

      // This helps determining shorthands
      while(parts.length > 3) {
        parts.pop();

        var shorthand = parts.join('-');

        if(supported(shorthand) && properties.indexOf(shorthand) === -1) {
          properties.push(shorthand);
        }
      }
    }
  },
  supported = function(property) {
    return StyleFix.camelCase(property) in dummy;
  }

  // Some browsers have numerical indices for the properties, some don't
  if(style.length > 0) {
    for(var i=0; i<style.length; i++) {
      iterate(style[i])
    }
  }
  else {
    for(var property in style) {
      iterate(StyleFix.deCamelCase(property));
    }
  }

  // Find most frequently used prefix
  var highest = {uses:0};
  for(var prefix in prefixes) {
    var uses = prefixes[prefix];

    if(highest.uses < uses) {
      highest = {prefix: prefix, uses: uses};
    }
  }

  self.prefix = '-' + highest.prefix + '-';
  self.Prefix = StyleFix.camelCase(self.prefix);

  self.properties = [];

  // Get properties ONLY supported with a prefix
  for(var i=0; i<properties.length; i++) {
    var property = properties[i];

    if(property.indexOf(self.prefix) === 0) { // we might have multiple prefixes, like Opera
      var unprefixed = property.slice(self.prefix.length);

      if(!supported(unprefixed)) {
        self.properties.push(unprefixed);
      }
    }
  }

  // IE fix
  if(self.Prefix == 'Ms'
    && !('transform' in dummy)
    && !('MsTransform' in dummy)
    && ('msTransform' in dummy)) {
    self.properties.push('transform', 'transform-origin');
  }

  self.properties.sort();
})();

/**************************************
 * Values
 **************************************/
(function() {
// Values that might need prefixing
var functions = {
  'linear-gradient': {
    property: 'backgroundImage',
    params: 'red, teal'
  },
  'calc': {
    property: 'width',
    params: '1px + 5%'
  },
  'element': {
    property: 'backgroundImage',
    params: '#foo'
  },
  'cross-fade': {
    property: 'backgroundImage',
    params: 'url(a.png), url(b.png), 50%'
  }
};

functions['repeating-linear-gradient'] =
functions['repeating-radial-gradient'] =
functions['radial-gradient'] =
functions['linear-gradient'];

// Note: The properties assigned are just to *test* support.
// The keywords will be prefixed everywhere.
var keywords = {
  'initial': 'color',
  'zoom-in': 'cursor',
  'zoom-out': 'cursor',
  'box': 'display',
  'flexbox': 'display',
  'inline-flexbox': 'display',
  'flex': 'display',
  'inline-flex': 'display',
  'grid': 'display',
  'inline-grid': 'display',
  'min-content': 'width'
};

self.functions = [];
self.keywords = [];

var style = document.createElement('div').style;

function supported(value, property) {
  style[property] = '';
  style[property] = value;

  return !!style[property];
}

for (var func in functions) {
  var test = functions[func],
    property = test.property,
    value = func + '(' + test.params + ')';

  if (!supported(value, property)
    && supported(self.prefix + value, property)) {
    // It's supported, but with a prefix
    self.functions.push(func);
  }
}

for (var keyword in keywords) {
  var property = keywords[keyword];

  if (!supported(keyword, property)
    && supported(self.prefix + keyword, property)) {
    // It's supported, but with a prefix
    self.keywords.push(keyword);
  }
}

})();

/**************************************
 * Selectors and @-rules
 **************************************/
(function() {

var
selectors = {
  ':read-only': null,
  ':read-write': null,
  ':any-link': null,
  '::selection': null
},

atrules = {
  'keyframes': 'name',
  'viewport': null,
  'document': 'regexp(".")'
};

self.selectors = [];
self.atrules = [];

var style = root.appendChild(document.createElement('style'));

function supported(selector) {
  style.textContent = selector + '{}';  // Safari 4 has issues with style.innerHTML

  return !!style.sheet.cssRules.length;
}

for(var selector in selectors) {
  var test = selector + (selectors[selector]? '(' + selectors[selector] + ')' : '');

  if(!supported(test) && supported(self.prefixSelector(test))) {
    self.selectors.push(selector);
  }
}

for(var atrule in atrules) {
  var test = atrule + ' ' + (atrules[atrule] || '');

  if(!supported('@' + test) && supported('@' + self.prefix + test)) {
    self.atrules.push(atrule);
  }
}

root.removeChild(style);

})();

// Properties that accept properties as their value
self.valueProperties = [
  'transition',
  'transition-property'
]

// Add class for current prefix
root.className += ' ' + self.prefix;

StyleFix.register(self.prefixCSS);

})(document.documentElement);


text:
#!/usr/bin/python3
from flipflop import WSGIServer
from schort import app

if __name__ == '__main__':
    WSGIServer(app).run()

repo_name: sqozz/schort

repo_language: Python

mime_type: text/html


text:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import schort
application = schort.app.wsgi_app


text:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from flask import Flask, render_template, url_for, request, redirect, abort, escape
import sqlite3, random, string, time, hashlib, base64
from urllib.parse import urlparse

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
@app.route('/<shortLink>', methods=['GET', 'POST'])
def short(shortLink=""):
	if request.method == "GET" or request.method == "HEAD":
		if shortLink:
			noauto = shortLink[-1] == "+"
			if noauto: shortLink = shortLink[:-1]
			url = retrieveUrlFromShortLink(shortLink)
			if url is not None:
				if "resolve" in request.args:
					return escape(url)
				else:
					if noauto:
						url = str(escape(url))
						html = "" + url + ""
						return html
					else:
						return redirect(url, code=301) # Redirect to long URL saved in the database
			else:
				return render_template("index.html", name=shortLink, message="Enter long URL for "+ request.url_root + shortLink+":", message_type="info"), 404 # Custom link page
		else:
			return render_template("index.html", name=shortLink) # Landing page
	elif request.method == "POST": # Someone submitted a new link to short
		longUrl = request.form.get("url", "")
		wishId = request.form.get("wishId")
		if len(longUrl) <= 0:
			abort(400)

		databaseId = insertIdUnique(longUrl, idToCheck=wishId)
		return request.url_root + databaseId # Short link in plain text

def retrieveUrlFromShortLink(shortLink):
	conn = sqlite3.connect("data/links.sqlite")
	c = conn.cursor()
	result = c.execute ('SELECT longLink FROM links WHERE shortLink=?', (shortLink, )).fetchone()
	conn.close()
	if result:
		url = result[0]
		parsedUrl = urlparse(url)
		if parsedUrl.scheme == "":
			url = "http://" + url
		return url
	else:
		return None

def insertIdUnique(longUrl, idToCheck=None):
	hashUrl = hashlib.sha256(longUrl.encode()).digest()
	base64Url = base64.urlsafe_b64encode(hashUrl).decode()
	if idToCheck == None or idToCheck == "":
		idToCheck = base64Url[:4]

	conn = sqlite3.connect("data/links.sqlite")
	c = conn.cursor()
	try:
		c.execute('INSERT INTO links VALUES (?, ?, ?, ?, ?)', (idToCheck, longUrl, int(time.time()), request.remote_addr, "default" ))
		databaseId = idToCheck
		conn.commit()
		conn.close()
	except sqlite3.IntegrityError as e:
		print("Hash already exists, does the long URL matches?")
		longUrlDb = c.execute('SELECT * FROM links WHERE shortLink=?', (idToCheck, )).fetchone()
		if longUrl == longUrlDb[1]:
			print(longUrl + " is already in database with id " + idToCheck + ". Serving old id…")
			databaseId = idToCheck
		else:
			print("Found real hash collision for " + longUrl + " and " + longUrlDb[1])
			conn.commit()
			conn.close()
			if len(base64Url) - 1 >= len(idToCheck) + 1:
				databaseId = insertIdUnique(longUrl, idToCheck=base64Url[:len(idToCheck)+1])
			else:
				print("Can't produce a long enough hash from the new link to be unique. This should never happen")
				print("Bailing out, you are on your own. Good luck.")
				print("=========================================================================================")
				abort(500)

	return databaseId

def initDB():
	conn = sqlite3.connect("data/links.sqlite")
	c = conn.cursor()
	c.execute('''CREATE TABLE IF NOT EXISTS links (shortLink UNIQUE NOT NULL, longLink, timestamp, ip, redirectMethod);''')
	conn.commit()
	conn.close()

if __name__ == '__main__':
	initDB()
	app.run(debug=True) # If you call this file directly it will always run in debug mode. THIS IS VERY DANGEROUS!

# vim: noexpandtab:ts=2:sw=2:sts=2


text: Flask


text:
# schort - It's just a tiny link shortener
## Installation instructions:
1. Create a user and adjust permissions to write at least into /opt/schort/data
    1. `sudo mkdir -p /opt/schort`
    2. `sudo groupadd schort`
    3. `sudo useradd -d /opt/schort -g schort -M -s /sbin/nologin schort`
    4. `sudo chown -R schort:schort /opt/schort`
    5. `sudo -u schort git clone https://github.com/sqozz/schort.git /opt/schort`
    6. `sudo chmod 770 /opt/schort/data`
2. Install requirements:
    1. `sudo -u schort virtualenv /opt/schort/venv`
    2. `sudo -u schort -s`
    3. `source ./venv/bin/activate`
    4. `pip install -r requirements.txt`
3. Configure your wsgi or fcgi server:
    1. Check the docs of your preferred server. E.g. gunicorn, uwsgi, fastcgi, …
    2. uwsgi example: `/usr/bin/uwsgi --master --daemonize /dev/null --disable-logging --plugin python39 --wsgi-file /opt/schort/schort.wsgi --post-buffering 1 --enable-threads --socket /tmp/uwsgi_schort.sock --processes 1 --fileserve-mode /opt/schort/schort.wsgi --pidfile /var/run/uwsgi_schort/schort.pid`
4. Configure your webserver that he talks to your wsgi/fcgi server:
    1. Check the docs of your preferred webserver. E.g. nginx, apache, …
    2. nginx example (you should really look into securing your server with https):

```
server {
	listen 80;
	listen [::]:80;
	server_name schort.your.domain;

	sendfile on;
	client_max_body_size 20M;
	keepalive_timeout 0;

	location / { try_files $uri @schort; }
	location @schort {
		include uwsgi_params;
		uwsgi_pass unix:/tmp/uwsgi_schort.sock;
	}
}
```

## Requirements:

| Module        | Explanation   |
| ------------- |---------------|
| Flask         | Flask handels all HTTP-stuff in this application |
| sqlite3       | In gentoo this useflag needs to be set while compiling python3     |

## µWSGI

The schort.wsgi file can be set as UWSGI_PROGRAM if you use uWSGI.
Keep in mind, that the UWSGI_DIR needs to be set to the path where schort.py resists.
This is because schort is not installed in a global scope. Since schort.wsgi imports schort.py it needs his workspace in the same folder.


text:
#!/usr/bin/env python3
import os
import unittest
import requests
from urllib import parse

BASE_URL="http://localhost:5000"

class WebTestCase(object):
	def assertPostReq(self, url, data = {}):
		req = requests.post(url, data=data)
		self.assertEqual(req.status_code, 200, msg="Post request unsuccessful")
		return req

	def assertGetReq(self, url, params = {}):
		req = requests.get(url, params=params)
		self.assertEqual(req.status_code, 200, msg="Get request unsuccessful")
		return req

	def assertGetStatusReq(self, expected_status, url, params = {}):
		req = requests.get(url, params=params, allow_redirects=False)
		self.assertEqual(req.status_code, expected_status, msg="Returned status code does not match the expected one")
		return req

	def assertHeadReq(self, url, params = {}):
		req = requests.head(url, params=params)
		self.assertEqual(req.status_code, 200, msg="Head request unsuccessful")
		return req

	def assertHeadStatusReq(self, expected_status, url, params = {}):
		req = requests.head(url, params=params, allow_redirects=False)
		self.assertEqual(req.status_code, expected_status, msg="Returned status code does not match the expected one")
		return req

class SchortBasicTests(unittest.TestCase, WebTestCase):
	def test_entry_page(self):
		"""HTML exists in root page"""
		req = self.assertGetReq(BASE_URL + "/")
		content = req.text
		self.assertNotEqual(len(content), 0, msg="Get request content was empty.")
		self.assertRegex(content, ".*\<html.*", msg="Didn't find an opening <html tag in the response.")
		self.assertRegex(content, ".*\<div.*", msg="Didn't find any opening <div tag in the response.")

class SchortRegressionTests(unittest.TestCase, WebTestCase):
	def test_empty_wish_id(self):
		"""Test a request with an empty wish-URL"""
		req = self.assertPostReq(BASE_URL + "/", data={"url" : "https://github.com/sqozz/schort", "wishId" : ""})
		short_url = req.text
		self.assertNotEqual(short_url, BASE_URL + "/", msg="Created short link cannot be equal to the root URL")

	def test_empty_wish_id(self):
		"""Test a request with no wishId as all"""
		req = self.assertPostReq(BASE_URL + "/", data={"url" : "https://github.com/sqozz/schort"})
		self.assertEqual(req.status_code, 200, msg="Could not handle a request without wishId in the parameter-list")

	def test_empty_url(self):
		"""Test an empty user supplied URL"""
		req = requests.post(BASE_URL + "/", data={"url": ""})
		self.assertEqual(req.status_code, 400, msg="Could not handle a request with empty url")
		req = requests.post(BASE_URL + "/", data={})
		self.assertEqual(req.status_code, 400, msg="Could not handle a request without a url at all")

class SchortShortLinkCase(object):
	pass
	shortID = ""
	shortDest = ""
	req = None

	def test_redirect(self):
		"""Test basic redirecting capabilites of schort"""
		self.assertNotEqual(len(self.shortID), 0)
		req = self.assertGetStatusReq(301, BASE_URL + "/" + self.shortID)
		loc = req.headers.get("location")
		self.assertEqual(loc, self.shortDest)

	def test_redirect_head(self):
		"""Test basic redirecting capability of schort for head requests"""
		req = self.assertHeadStatusReq(301, BASE_URL + "/" + self.shortID)
		loc = req.headers.get("location")
		self.assertEqual(loc, self.shortDest)

	def test_redirect_follow(self):
		"""Test if the requests-lib can follow the redirect"""
		req = requests.get(BASE_URL + "/" + self.shortID, allow_redirects=True)
		req.url = self.shortDest

	def test_redirect_follow_head(self):
		"""Test if the request lib can follow the redirect in a HEAD request"""
		req = requests.head(BASE_URL + "/" + self.shortID, allow_redirects=True)
		req.url = self.shortDest

	def test_resolve(self):
		"""Test the resolve parameter"""
		req = self.assertGetReq(BASE_URL + "/" + self.shortID, params = {"resolve" : ""})
		self.assertEqual(req.text, self.shortDest)

	def test_HTMLresolve(self):
		"""Test HTML displaying of the shortened URL"""
		HTML_keyword = "+"
		req = self.assertGetReq(BASE_URL + "/" + self.shortID + HTML_keyword)
		self.assertRegex(req.text, "()({url})()".format(url=self.shortDest), msg="Returned HTML does not match the regex")

class SchortCustomIdTests(unittest.TestCase, SchortShortLinkCase, WebTestCase):
	def setUp(self):
		self.shortID = "custom_user_supplied_url"
		self.shortDest = "https://github.com/sqozz/schort"
		self.req = requests.post(BASE_URL + "/", data={"url" : self.shortDest, "wishId" : self.shortID})

	def test_create(self):
		"""Test short link creation with a custom supplied wish-id"""
		short_url = self.req.text
		self.assertEqual(short_url, BASE_URL + "/" + self.shortID)
		self.assertEqual(self.req.status_code, 200)

class SchortRandomIdTests(unittest.TestCase, SchortShortLinkCase, WebTestCase):
	def setUp(self):
		"""Set up a short url with a randomly assigned id"""
		self.shortDest = "https://github.com/sqozz/schort"
		self.req = requests.post(BASE_URL + "/", data={"url" : self.shortDest})
		aquiredId = parse.urlparse(self.req.text)
		aquiredId = aquiredId.path.replace("/", "", 1)
		self.shortID = aquiredId

	def test_create(self):
		"""Test short link creation with a randomly assigned id"""
		self.assertNotEqual(len(self.shortID), 0)
		self.assertEqual(self.req.status_code, 200, msg="Link creation was unsuccessful")

if __name__ == '__main__':
	suite = unittest.TestSuite()
	suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SchortBasicTests))
	suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SchortRegressionTests))
	suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SchortCustomIdTests))
	suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SchortRandomIdTests))
	unittest.TextTestRunner(verbosity=2).run(suite)

# vim: noexpandtab:ts=2:sw=2:sts=2


text:
html, body {
	height: 100%;
	width: 100%;
	margin: 0px;
}

body {
	display: flex;
	justify-content: center;
	align-items: center;
	font-family: monospace;
}

#main {
	width: 40%;
}

#url_input {
	border: 1px solid rgb(200, 200, 200);
	height: 1.3em;
	margin: 0px;
	padding-left: 10px;
	flex: 1;
	font-size: 0.7em;
}

#short_button {
	border: 1px solid rgb(200, 200, 200);
	height: 1.9em;
	margin: 0px;
	margin-left: 10px;
	font-family: monospace;
	font-size: 0.5em;
}

#short_form {
	font-size: 3em;
	display: flex;
	justify-content: center;
	align-items: center;
}

#url_text {
	margin-right: 10px;
}

#message {
	text-align: center;
	margin-bottom: 5px;
	word-wrap: break-word;
}

.error {
	color: red;
}

.info {
	color: #848400;
}

#infobar {
	font-size: 1em;
	display: flex;
	margin-top: 10px;
}

a {
	color: rgb(150, 150, 150);
	display: block;
	text-decoration: none;
}

a:hover {
	color: black;
	text-decoration: underline;
}

a:hover * {
	fill: black;
}

#githubLink {
       display: flex;
}

#githubLink svg {
	margin-right: 5px;
	margin-left: 5px;
	fill: rgb(150, 150, 150);
	width: 1.2em;
	height: 1.2em;
}

.infoitem {
	flex: 1;
	display: flex;
	justify-content: center;
	border-right: 1px solid #cfcfcf;
}

.infoitem:last-child {
	border-right: none;
}

span .description {
	color: black;
	display: none;
	opacity: 0;
	text-align: left;
	margin-left: -6%;
	max-width: 300px;
	margin-top: 10px;
	padding: 3px;
	box-shadow: 3px 3px 10px gray;
	max-height: 35%;
	overflow-y: auto;
}

a:focus .description {
	position: absolute;
	display: inline;
	opacity: 1;
	background-color: white;
	pointer-events: auto;
}

a:focus > div {
	pointer-events: none;
}

ol, ul {
	padding-left: 15px;
	margin-top: 0px;
	margin-bottom: 5px;
}

.description li {
	font-weight: bold;
}

.description li + div {
	margin-left: 3px;
	margin-right: 5px;
}

.description {
	background:
		/* Shadow covers */
		linear-gradient(white 30%, rgba(255,255,255,0)),
		linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,

		/* Shadows */
		radial-gradient(50% 0, farthest-side, rgba(0,0,0,.3), rgba(0,0,0,0)),
		radial-gradient(50% 100%,farthest-side, rgba(0,0,0,.3), rgba(0,0,0,0)) 0 100%;

	background:
		/* Shadow covers */
		linear-gradient(white 30%, rgba(255,255,255,0)),
		linear-gradient(rgba(255,255,255,0), white 70%) 0 100%,

		/* Shadows */
		radial-gradient(farthest-side at 50% 0, rgba(0,0,0,.3), rgba(0,0,0,0)),
		radial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.3), rgba(0,0,0,0)) 0 100%;
	background-repeat: no-repeat;
	background-color: white;
	background-size: 100% 40px, 100% 40px, 100% 14px, 100% 14px;

	background-attachment: local, local, scroll, scroll;
}


text:
<!doctype html>

		<meta charset="utf-8">
		<title>schort</title>
			<link rel="stylesheet" href="static/css/index.css">

				{{message}}

			<form action="/" method="post" id="short_form">
				URL:
				<input type="url" name="url" id="url_input" required>
				<input type="hidden" name="wishId" value="{{name}}">
				<input type="submit" value="Schort!" id="short_button">
			</form>

API

API

There is no dedicated API for schort. Just make a POST-request to / with the parameter "url" set to your desired destination. Schort will generate an id for your and gives back the short link in plain text.

Creation of new links

If you wish to create a custom URL with your script, you can also set the parameter "wishId". If the requested id is free, schort will print out the short link with your requested id. If not, schort will just generate one and print that one out.

Resolving links

To resolve a link you just have to make a GET-request and add the "resolve" parameter. The value of this parameter doesn't matter and gets ignored. This makes schort to print out a plain text string of the original URL.

FAQ

Can i produce custom links?

Just open /&ltyourWishId&gt. If you get no redirect, you can enter your URL according to your custom id.

How can i inspect an shortened link?

Append a plus sign ("+") to your short URL. You'll receive a clickable link to the remote destination.

How can my script resolve a shortened link?

Take a look at the API.

				<svg aria-hidden="true" class="octicon octicon-mark-github" height="15" role="img" version="1.1" viewBox="0 0 16 16" width="15"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59 0.4 0.07 0.55-0.17 0.55-0.38 0-0.19-0.01-0.82-0.01-1.49-2.01 0.37-2.53-0.49-2.69-0.94-0.09-0.23-0.48-0.94-0.82-1.13-0.28-0.15-0.68-0.52-0.01-0.53 0.63-0.01 1.08 0.58 1.23 0.82 0.72 1.21 1.87 0.87 2.33 0.66 0.07-0.52 0.28-0.87 0.51-1.07-1.78-0.2-3.64-0.89-3.64-3.95 0-0.87 0.31-1.59 0.82-2.15-0.08-0.2-0.36-1.02 0.08-2.12 0 0 0.67-0.21 2.2 0.82 0.64-0.18 1.32-0.27 2-0.27 0.68 0 1.36 0.09 2 0.27 1.53-1.04 2.2-0.82 2.2-0.82 0.44 1.1 0.16 1.92 0.08 2.12 0.51 0.56 0.82 1.27 0.82 2.15 0 3.07-1.87 3.75-3.65 3.95 0.29 0.25 0.54 0.73 0.54 1.48 0 1.07-0.01 1.93-0.01 2.2 0 0.21 0.15 0.46 0.55 0.38C13.71 14.53 16 11.53 16 8 16 3.58 12.42 0 8 0z"></path></svg> github.com


text:
require "pdf"

p = PDF.new()

helv = p:new_font{ name = "Helvetica" }
times = p:new_font{ name = "Times-Roman" }

page = p:new_page()

page:setrgbcolor("stroke", 0.5, 0, 1)
page:moveto(150, 250)
page:lineto(150, 350)
page:stroke()

page:setrgbcolor("stroke", 0, 1, 0.3)
page:moveto(250, 250)
page:lineto(250, 350)
page:stroke()

page:save()

page:begin_text()
page:set_font(helv, 24)
page:set_text_pos(100, 100)
page:show("Hello, world!")
page:end_text()

page:begin_text()
page:set_font(times, 12)

for i = 1, 40 do
	page:save()
	page:translate(400, 400)
	page:rotate((3.14159 / 40) * i)
	page:moveto(0, 0)
	page:lineto(25, 0)
	page:stroke()
	page:set_text_pos(0, 0)
	page:show("<<and goodbye>>")
	page:restore()
end

page:end_text()

page:restore()

page2 = p:new_page()
page2:moveto(250, 250)
page2:lineto(350, 250)
page2:stroke()

page2:begin_text()
page2:set_font(times, 24)
page2:set_text_pos(250, 250)
page2:show("Text")
page2:end_text()

draw_circle = function(p, x, y, r)
	local k = 0.5522847498 * r

	p:moveto(x - r, y)
	p:curveto(x - r, y + k, x - k, y + r, x, y + r)
	p:curveto(x + k, y + r, x + r, y + k, x + r, y)
	p:curveto(x + r, y - k, x + k, y - r, x, y - r)
	p:curveto(x - k, y - r, x - r, y - k, x - r, y)

end

draw_circle(page2, 300, 400, 100)
page2:stroke()

page:add()
page2:add()

p:write("test.pdf")

repo_language: Lua


text:
PDF = {}
PDF.new = function()
	local pdf = {}		-- instance variable
	local page = {}		-- array of page descriptors
	local object = {}	-- array of object contents
	local xref_table_offset	-- byte offset of xref table

	local catalog_obj	-- global catalog object
	local pages_obj		-- global pages object
	local procset_obj	-- global procset object

	--
	-- Private functions.
	--

	local add = function(obj)
		table.insert(object, obj)
		obj.number = table.getn(object)
		return obj
	end

	local get_ref = function(obj)
		return string.format("%d 0 R", obj.number)
	end

	local write_object
	local write_direct_object
	local write_indirect_object

	write_object = function(fh, obj)
		if type(obj) == "table" and obj.datatype == "stream" then
			write_indirect_object(fh, obj)
		else
			write_direct_object(fh, obj)
		end
	end

	write_direct_object = function(fh, obj)
		if type(obj) ~= "table" then
			fh:write(obj .. "\n")
		elseif obj.datatype == "dictionary" then
			local k, v

			fh:write("<<\n")
			for k, v in pairs(obj.contents) do
				fh:write(string.format("/%s ", k))
				write_object(fh, v)
			end
			fh:write(">>\n")
		elseif obj.datatype == "array" then
			local v

			fh:write("[\n")
			for _, v in ipairs(obj.contents) do
				write_object(fh, v)
			end
			fh:write("]\n")
		elseif obj.datatype == "stream" then
			local len = 0

			if type(obj.contents) == "string" then
				len = string.len(obj.contents)
			else -- assume array
				local i, str

				for i, str in ipairs(obj.contents) do
					len = len + string.len(str) + 1
				end
			end

			fh:write(string.format("<< /Length %d >>\n", len))
			fh:write("stream\n")

			if type(obj.contents) == "string" then
				fh:write(obj.contents)
			else -- assume array
				local i, str

				for i, str in ipairs(obj.contents) do
					fh:write(str)
					fh:write("\n")
				end
			end

			fh:write("endstream\n")
		end
	end

	write_indirect_object = function(fh, obj)
		obj.offset = fh:seek()
		fh:write(string.format("%d %d obj\n", obj.number, 0))
		write_direct_object(fh, obj)
		fh:write("endobj\n")
	end

	local write_header = function(fh)
		fh:write("%PDF-1.0\n")
	end

	local write_body = function(fh)
		local i, obj

		for i, obj in ipairs(object) do
			write_indirect_object(fh, obj)
		end
	end

	local write_xref_table = function(fh)
		local i, obj

		xref_table_offset = fh:seek()
		fh:write("xref\n")
		fh:write(string.format("%d %d\n", 1, table.getn(object)))
		for i, obj in ipairs(object) do
			fh:write(
			    string.format("%010d %05d n \n", obj.offset, 0)
			)
		end
	end

	local write_trailer = function(fh)
		fh:write("trailer\n")
		fh:write("<<\n")
		fh:write(string.format("/Size %d\n", table.getn(object)))
		fh:write("/Root " .. get_ref(catalog_obj) .. "\n")
		fh:write(">>\n")
		fh:write("startxref\n")
		fh:write(string.format("%d\n", xref_table_offset))
		fh:write("%%EOF\n")
	end

	--
	-- Instance methods.
	--

	pdf.new_font = function(pdf, tab)
		local subtype = tab.subtype or "Type1"
		local name = tab.name or "Helvetica"
		local font_obj = add {
			datatype = "dictionary",
			contents = {
				Type = "/Font",
				Subtype = "/" .. subtype,
				BaseFont = "/" .. name,
			}
		}
		return font_obj
	end

	pdf.new_page = function(pdf)
		local pg = {}		-- instance variable
		local contents = {}	-- array of operation strings
		local used_font = {}	-- fonts used on this page

		--
		-- Private functions.
		--

		local use_font = function(font_obj)
			local i, f

			for i, f in ipairs(used_font) do
				if font_obj == f then
					return "/F" .. i
				end
			end

			table.insert(used_font, font_obj)
			return "/F" .. table.getn(used_font)
		end

		--
		-- Instance methods.
		--

		--
		-- Text functions.
		--

		pg.begin_text = function(pg)
			table.insert(contents, "BT")
		end

		pg.end_text = function(pg)
			table.insert(contents, "ET")
		end

		pg.set_font = function(pg, font_obj, size)
			table.insert(contents,
			    string.format("%s %f Tf",
			        use_font(font_obj), size)
			)
		end

		pg.set_text_pos = function(pg, x, y)
			table.insert(contents,
			    string.format("%f %f Td", x, y)
			)
		end

		pg.show = function(pg, str)
			table.insert(contents,
			    string.format("(%s) Tj", str)
			)
		end

		pg.set_char_spacing = function(pg, spc)
			table.insert(contents,
			    string.format("%f Tc", spc)
			)
		end

		--
		-- Graphics - path drawing functions.
		--

		pg.moveto = function(pg, x, y)
			table.insert(contents,
			    string.format("%f %f m", x, y)
			)
		end

		pg.lineto = function(pg, x, y)
			table.insert(contents,
			    string.format("%f %f l", x, y)
			)
		end

		pg.curveto = function(pg, x1, y1, x2, y2, x3, y3)
			local str

			if x3 and y3 then
				str = string.format("%f %f %f %f %f %f c",
				x1, y1, x2, y2, x3, y3)
			else
				str = string.format("%f %f %f %f v",
				x1, y1, x2, y2)
			end

			table.insert(contents, str)
		end

		pg.rectangle = function(pg, x, y, w, h)
			table.insert(contents,
			    string.format("%f %f %f %f re",
			    x, y, w, h)
			)
		end

		--
		-- Graphics - colours.
		--

		pg.setgray = function(pg, which, gray)
			assert(which == "fill" or which == "stroke")
			assert(gray >= 0 and gray <= 1)
			if which == "fill" then
				table.insert(contents,
				    string.format("%d g", gray)
				)
			else
				table.insert(contents,
				    string.format("%d G", gray)
				)
			end
		end

		pg.setrgbcolor = function(pg, which, r, g, b)
			assert(which == "fill" or which == "stroke")
			assert(r >= 0 and r <= 1)
			assert(g >= 0 and g <= 1)
			assert(b >= 0 and b <= 1)
			if which == "fill" then
				table.insert(contents,
				    string.format("%f %f %f rg", r, g, b)
				)
			else
				table.insert(contents,
				    string.format("%f %f %f RG", r, g, b)
				)
			end
		end

		pg.setcmykcolor = function(pg, which, c, m, y, k)
			assert(which == "fill" or which == "stroke")
			assert(c >= 0 and c <= 1)
			assert(m >= 0 and m <= 1)
			assert(y >= 0 and y <= 1)
			assert(k >= 0 and k <= 1)
			if which == "fill" then
				table.insert(contents,
				    string.format("%f %f %f %f k", c, m, y, k)
				)
			else
				table.insert(contents,
				    string.format("%f %f %f %f K", c, m, y, k)
				)
			end
		end

		--
		-- Graphics - line options.
		--

		pg.setflat = function(pg, i)
			assert(i >= 0 and i <= 100)
			table.insert(contents,
			    string.format("%d i", i)
			)
		end

		pg.setlinecap = function(pg, j)
			assert(j == 0 or j == 1 or j == 2)
			table.insert(contents,
			    string.format("%d J", j)
			)
		end

		pg.setlinejoin = function(pg, j)
			assert(j == 0 or j == 1 or j == 2)
			table.insert(contents,
			    string.format("%d j", j)
			)
		end

		pg.setlinewidth = function(pg, w)
			table.insert(contents,
			    string.format("%d w", w)
			)
		end

		pg.setmiterlimit = function(pg, m)
			assert(m >= 1)
			table.insert(contents,
			    string.format("%d M", m)
			)
		end

		pg.setdash = function(pg, array, phase)
			local str = ""
			local v

			for _, v in ipairs(array) do
				str = str .. v .. " "
			end

			table.insert(contents,
			    string.format("[%s] %d d", str, phase)
			)
		end

		--
		-- Graphics - path-terminating functions.
		--

		pg.stroke = function(pg)
			table.insert(contents, "S")
		end

		pg.closepath = function(pg)
			table.insert(contents, "h")
		end

		pg.fill = function(pg)
			table.insert(contents, "f")
		end

		pg.newpath = function(pg)
			table.insert(contents, "n")
		end

		pg.clip = function(pg) -- no effect until next newpath
			table.insert(contents, "W")
		end

		--
		-- Graphics - state save/restore.
		--

		pg.save = function(pg)
			table.insert(contents, "q")
		end

		pg.restore = function(pg)
			table.insert(contents, "Q")
		end

		--
		-- Graphics - CTM functions.
		--
		pg.transform = function(pg, a, b, c, d, e, f) -- aka concat
			table.insert(contents,
			    string.format("%f %f %f %f %f %f cm",
			        a, b, c, d, e, f)
			)
		end

		pg.translate = function(pg, x, y)
			pg:transform(1, 0, 0, 1, x, y)
		end

		pg.scale = function(pg, x, y)
			if not y then y = x end
			pg:transform(x, 0, 0, y, 0, 0)
		end

		pg.rotate = function(pg, theta)
			local c, s = math.cos(theta), math.sin(theta)
			pg:transform(c, s, -1 * s, c, 0, 0)
		end

		pg.skew = function(pg, tha, thb)
			local tana, tanb = math.tan(tha), math.tan(thb)
			pg:transform(1, tana, tanb, 1, 0, 0)
		end

		pg.add = function(pg)
			local contents_obj, this_obj, resources
			local i, font_obj

			contents_obj = add {
				datatype = "stream",
				contents = contents
			}

			resources = {
				datatype = "dictionary",
				contents = {
					Font = {
						datatype = "dictionary",
						contents = {}
					},
					ProcSet = get_ref(procset_obj)
				}
			}

			for i, font_obj in ipairs(used_font) do
				resources.contents.Font.contents["F" .. i] =
				    get_ref(font_obj)
			end

			this_obj = add {
				datatype = "dictionary",
				contents = {
					Type = "/Page",
					Parent = get_ref(pages_obj),
					Contents = get_ref(contents_obj),
					Resources = resources
				}
			}

			table.insert(pages_obj.contents.Kids.contents,
			    get_ref(this_obj))
			pages_obj.contents.Count = pages_obj.contents.Count + 1
		end

		table.insert(page, pg)
		return pg
	end

	pdf.write = function(pdf, file)
		local fh

		if type(file) == "string" then
			fh = assert(io.open(file, "w"))
		else
			fh = file
		end

		write_header(fh)
		write_body(fh)
		write_xref_table(fh)
		write_trailer(fh)

		fh:close()
	end

	-- initialize... add a few objects that we know will exist.
	pages_obj = add {
		datatype = "dictionary",
		contents = {
			Type = "/Pages",
			Kids = {
				datatype = "array",
				contents = {}
			},
			Count = 0
		}
	}

	catalog_obj = add {
		datatype = "dictionary",
		contents = {
			Type = "/Catalog",
			Pages = get_ref(pages_obj)
		}
	}

	procset_obj = add {
		datatype = "array",
		contents = { "/PDF", "/Text" }
	}

	return pdf
end


text:
pdf.lua
=======

This is a pure Lua library for low-level generation of PDF files.

This is code I wrote, oh gosh, about 10 years ago now, and I have no plans to
develop it further.  It is probably more valuable as a demonstration that it's
really not that difficult to create a PDF file, than as a serious piece of
equipment.  However, I just tried the test script with Lua 5.1.5, using
Firefox 33.0 as the PDF viewer, and it still works.

This work is in the public domain; see the file UNLICENSE for details.


text:
# frozen_string_literal: true

require 'rubygems'

task default: [:spec]

namespace :gem do
  desc "Build the json-ld-#{File.read('VERSION').chomp}.gem file"
  task :build do
    sh "gem build json-ld.gemspec && mv json-ld-#{File.read('VERSION').chomp}.gem pkg/"
  end

  desc "Release the json-ld-#{File.read('VERSION').chomp}.gem file"
  task :release do
    sh "gem push pkg/json-ld-#{File.read('VERSION').chomp}.gem"
  end
end

require 'rspec/core/rake_task'
desc 'Run specifications'
RSpec::Core::RakeTask.new(:spec) do |spec|
  spec.rspec_opts = %w[--options spec/spec.opts] if File.exist?('spec/spec.opts')
end

desc "Generate schema.org context"
task :schema_context do
  `
    script/gen_context https://schema.org/docs/schema_org_rdfa.html \
      --vocab http://schema.org/ \
      --prefix 'schema http://schema.org/' \
      --body --hier \
      --o etc/schema.org.jsonld
  `
end

desc "Create concatenated test manifests"
file "etc/manifests.nt" do
  require 'rdf'
  require 'json/ld'
  require 'rdf/ntriples'
  graph = RDF::Graph.new do |g|
    %w[ https://w3c.github.io/json-ld-api/tests/compact-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/expand-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/flatten-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/fromRdf-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/html-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/remote-doc-manifest.jsonld
        https://w3c.github.io/json-ld-api/tests/toRdf-manifest.jsonld
        https://w3c.github.io/json-ld-framing/tests/frame-manifest.jsonld].each do |man|
      puts "load #{man}"
      g.load(man, unique_bnodes: true)
    end
  end
  puts "write"
  RDF::NTriples::Writer.open("etc/manifests.nt", unique_bnodes: true, validate: false) { |w| w << graph }
end

# Presentation building
namespace :presentation do
  desc "Clean presentation files"
  task :clean do
    FileUtils.rm %w[compacted expanded framed].map { |f| "presentation/dbpedia/#{f}.jsonld" }
  end

  desc "Build presentation files"
  task build: %w[
    presentation/dbpedia/expanded.jsonld
    presentation/dbpedia/compacted.jsonld
    presentation/dbpedia/framed.jsonld
  ]

  desc "Build expanded example"
  file "presentation/dbpedia/expanded.jsonld" => %w[
    presentation/dbpedia/orig.jsonld
    presentation/dbpedia/expanded-context.jsonld
  ] do
    system(%w[
      script/parse
      --expand presentation/dbpedia/orig.jsonld
      --context presentation/dbpedia/expanded-context.jsonld
      -o presentation/dbpedia/expanded.jsonld
    ].join(" "))
  end

  desc "Build compacted example"
  file "presentation/dbpedia/compacted.jsonld" => %w[
    presentation/dbpedia/expanded.jsonld
    presentation/dbpedia/compact-context.jsonld
  ] do
    system(%w[
      script/parse
      --compact presentation/dbpedia/expanded.jsonld
      --context presentation/dbpedia/compact-context.jsonld
      -o presentation/dbpedia/compacted.jsonld
    ].join(" "))
  end

  desc "Build framed example"
  file "presentation/dbpedia/framed.jsonld" => %w[
    presentation/dbpedia/expanded.jsonld
    presentation/dbpedia/frame.jsonld
  ] do
    system(%w[
      script/parse
      --frame presentation/dbpedia/frame.jsonld
      presentation/dbpedia/expanded.jsonld
      -o presentation/dbpedia/framed.jsonld
    ].join(" "))
  end
end

require 'yard'
namespace :doc do
  YARD::Rake::YardocTask.new
end

task default: :spec
task specs: :spec

repo_name: ruby-rdf/json-ld

repo_language: HTML


text:
#!/usr/bin/env ruby -rubygems
# frozen_string_literal: true

is_java = RUBY_PLATFORM == 'java'

Gem::Specification.new do |gem|
  gem.version               = File.read('VERSION').chomp

  gem.name                  = "json-ld"
  gem.homepage              = "https://github.com/ruby-rdf/json-ld"
  gem.license               = 'Unlicense'
  gem.summary               = "JSON-LD reader/writer for Ruby."
  gem.description           = "JSON::LD parses and serializes JSON-LD into RDF and implements expansion, compaction and framing API interfaces for the Ruby RDF.rb library suite."
  gem.metadata = {
    "documentation_uri" => "https://ruby-rdf.github.io/json-ld",
    "bug_tracker_uri" => "https://github.com/ruby-rdf/json-ld/issues",
    "homepage_uri" => "https://github.com/ruby-rdf/json-ld",
    "mailing_list_uri" => "https://lists.w3.org/Archives/Public/public-rdf-ruby/",
    "source_code_uri" => "https://github.com/ruby-rdf/json-ld",
    'rubygems_mfa_required' => 'true'
  }

  gem.authors               = ['Gregg Kellogg']
  gem.email                 = 'public-linked-json@w3.org'

  gem.platform              = Gem::Platform::RUBY
  gem.files                 = %w[AUTHORS README.md UNLICENSE VERSION] + Dir.glob('lib/**/*.rb')
  gem.bindir               = 'bin'
  gem.executables          = %w[jsonld]
  gem.require_paths         = %w[lib]

  gem.required_ruby_version = '>= 2.6'
  gem.requirements          = []
  gem.add_runtime_dependency     'htmlentities', '~> 4.3'
  gem.add_runtime_dependency     'json-canonicalization', '~> 0.3', '>= 0.3.2'
  gem.add_runtime_dependency     'link_header', '~> 0.0', '>= 0.0.8'
  gem.add_runtime_dependency     'multi_json',      '~> 1.15'
  gem.add_runtime_dependency     "rack",            '>= 2.2', '< 4'
  gem.add_runtime_dependency     'rdf',             '~> 3.2', '>= 3.2.10'
  gem.add_development_dependency 'jsonlint',        '~> 0.4'  unless is_java
  gem.add_development_dependency 'oj',              '~> 3.14' unless is_java
  gem.add_development_dependency 'rack-test',       '>= 1.1', '< 3'
  gem.add_development_dependency 'rdf-isomorphic',  '~> 3.2'
  gem.add_development_dependency 'rdf-spec',        '~> 3.2'
  gem.add_development_dependency 'rdf-trig',        '~> 3.2'
  gem.add_development_dependency 'rdf-turtle',      '~> 3.2'
  gem.add_development_dependency 'rdf-vocab',       '~> 3.2'
  gem.add_development_dependency 'rdf-xsd',         '~> 3.2'
  gem.add_development_dependency 'rspec',           '~> 3.12'
  gem.add_development_dependency 'rspec-its',       '~> 1.3'
  gem.add_development_dependency 'sinatra-linkeddata', '~> 3.2'
  gem.add_development_dependency 'yajl-ruby',       '~> 1.4' unless is_java
  gem.add_development_dependency 'yard', '~> 0.9'

  gem.post_install_message = nil
end


text:
# How to contribute

Community contributions are essential for keeping Ruby RDF great. We want to keep it as easy as possible to contribute changes that get things working in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.

## Development

This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration.

* create or respond to an issue on the [Github Repository](https://github.com/ruby-rdf/json-ld/issues)
* Fork and clone the repo:
  `git clone git@github.com:your-username/json-ld.git`
* Install bundle:
  `bundle install`
* Create tests in RSpec and make sure you achieve at least 90% code coverage for the feature your adding or behavior being modified.
* Push to your fork and [submit a pull request][pr].

## Do's and Dont's
* Do your best to adhere to the existing coding conventions and idioms.
* Don't use hard tabs, and don't leave trailing whitespace on any line.
  Before committing, run `git diff --check` to make sure of this.
* Do document every method you add using [YARD][] annotations. Read the
  [tutorial][YARD-GS] or just look at the existing code for examples.
* Don't touch the `.gemspec` or `VERSION` files. If you need to change them,
  do so on your private branch only.
* Do feel free to add yourself to the `CREDITS` file and the
  corresponding list in the the `README`. Alphabetical order applies.
* Don't touch the `AUTHORS` file. If your contributions are significant
  enough, be assured we will eventually add you in there.
* Do note that in order for us to merge any non-trivial changes (as a rule
  of thumb, additions larger than about 15 lines of code), we need an
  explicit [public domain dedication][PDD] on record from you,
  which you will be asked to agree to on the first commit to a repo within the organization.
  Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.

[YARD]:    https://yardoc.org/
[YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
[PDD]:              https://unlicense.org/#unlicensing-contributions
[pr]:      https://github.com/ruby-rdf/rdf/compare/


text: * Gregg Kellogg <gregg@greggkellogg.net>


text:
# frozen_string_literal: true

source "https://rubygems.org"
gem "nokogiri", '~> 1.13', '>= 1.13.4'

gemspec
gem 'json-canonicalization', git: "https://github.com/dryruby/json-canonicalization", branch: "develop"
gem 'rdf', git: "https://github.com/ruby-rdf/rdf", branch: "develop"

group :development do
  gem 'earl-report'
  gem 'ebnf', git: "https://github.com/dryruby/ebnf", branch: "develop"
  gem 'fasterer'
  gem 'json-ld-preloaded', github: "ruby-rdf/json-ld-preloaded", branch: "develop"
  gem 'ld-patch', github: "ruby-rdf/ld-patch", branch: "develop"
  gem 'linkeddata', git: "https://github.com/ruby-rdf/linkeddata", branch: "develop"
  gem 'rack-linkeddata', git: "https://github.com/ruby-rdf/rack-linkeddata", branch: "develop"
  gem 'rdf-aggregate-repo', git: "https://github.com/ruby-rdf/rdf-aggregate-repo", branch: "develop"
  gem 'rdf-isomorphic', git: "https://github.com/ruby-rdf/rdf-isomorphic", branch: "develop"
  gem 'rdf-json', github: "ruby-rdf/rdf-json", branch: "develop"
  gem 'rdf-microdata', git: "https://github.com/ruby-rdf/rdf-microdata", branch: "develop"
  gem 'rdf-n3', github: "ruby-rdf/rdf-n3", branch: "develop"
  gem 'rdf-normalize', github: "ruby-rdf/rdf-normalize", branch: "develop"
  gem 'rdf-rdfa', git: "https://github.com/ruby-rdf/rdf-rdfa", branch: "develop"
  gem 'rdf-rdfxml', git: "https://github.com/ruby-rdf/rdf-rdfxml", branch: "develop"
  gem 'rdf-reasoner', github: "ruby-rdf/rdf-reasoner", branch: "develop"
  gem 'rdf-spec', git: "https://github.com/ruby-rdf/rdf-spec", branch: "develop"
  gem 'rdf-tabular', github: "ruby-rdf/rdf-tabular", branch: "develop"
  gem 'rdf-trig', git: "https://github.com/ruby-rdf/rdf-trig", branch: "develop"
  gem 'rdf-trix', github: "ruby-rdf/rdf-trix", branch: "develop"
  gem 'rdf-turtle', git: "https://github.com/ruby-rdf/rdf-turtle", branch: "develop"
  gem 'rdf-vocab', git: "https://github.com/ruby-rdf/rdf-vocab", branch: "develop"
  gem 'rdf-xsd', git: "https://github.com/ruby-rdf/rdf-xsd", branch: "develop"
  gem 'ruby-prof', platforms: :mri
  gem 'shex', github: "ruby-rdf/shex", branch: "develop"
  gem 'sinatra-linkeddata', git: "https://github.com/ruby-rdf/sinatra-linkeddata", branch: "develop"
  gem 'sparql', git: "https://github.com/ruby-rdf/sparql", branch: "develop"
  gem 'sparql-client', git: "https://github.com/ruby-rdf/sparql-client", branch: "develop"
  gem 'sxp', git: "https://github.com/dryruby/sxp.rb", branch: "develop"
end

group :development, :test do
  gem 'benchmark-ips'
  gem 'psych', platforms: %i[mri rbx]
  gem 'rake'
  gem 'rubocop'
  gem 'rubocop-performance'
  gem 'rubocop-rspec'
  gem 'simplecov', '~> 0.21', platforms: :mri
  gem 'simplecov-lcov', '~> 0.8', platforms: :mri
end

group :debug do
  gem "byebug", platforms: :mri
end


text:
platform:
  Rubygems:
    rdf-isomorphic:
      tests:
        unmaintained: skip