_list.__init__(self)<EOL>self.name = "<STR_LIT>"<EOL>self.tags = []<EOL>for arg in args:<EOL><INDENT>if arg.__class__ == Color:<EOL><INDENT>self.append(arg)<EOL><DEDENT>if arg.__class__ == BaseColor:<EOL><INDENT>self.append(color(arg.r, arg.g, arg.b, mode="<STR_LIT>"))<EOL><DEDENT>try:<EOL><INDENT>self.name = arg.label<EOL>for r, g, b in arg:<EOL><INDENT>self.append(color(r, g, b, mode="<STR_LIT>"))<EOL><DEDENT><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT>if isinstance(arg, _list)or isinstance(arg, tuple):<EOL><INDENT>for clr in arg:<EOL><INDENT>if clr.__class__ == Color:<EOL><INDENT>self.append(clr)<EOL><DEDENT>if clr.__class__ == BaseColor:<EOL><INDENT>self.append(color(clr))<EOL><DEDENT><DEDENT><DEDENT>if isinstance(arg, (str, unicode)):<EOL><INDENT>if os.path.exists(arg):<EOL><INDENT>n = <NUM_LIT:10><EOL>if "<STR_LIT:n>" in kwargs.keys(): n = kwargs["<STR_LIT:n>"]<EOL>self.image_to_rgb(arg, n)<EOL><DEDENT>else:<EOL><INDENT>clr = Color(arg)<EOL>if not clr.is_transparent:<EOL><INDENT>self.append(clr)<EOL>self.name = arg<EOL><DEDENT>else:<EOL><INDENT>self.extend(self.context_to_rgb(arg))<EOL>self.tags = arg<EOL><DEDENT><DEDENT><DEDENT><DEDENT>if "<STR_LIT:name>" in kwargs.keys():<EOL><INDENT>self.name = kwargs["<STR_LIT:name>"]<EOL><DEDENT>if "<STR_LIT>" in kwargs.keys():<EOL><INDENT>self.tags = kwargs["<STR_LIT>"]<EOL><DEDENT>

Construct a list of colors.

Colors can be supplied as individual arguments,
or in a list or tuple:
ColorList(clr1, clr2)
ColorList([clr1, clr2])
ColorList((clr1, clr2))

You can also supply an object from inside a
web.kuler.search() or web.colr.search() list.

Or a string with a named color, a descriptive feel,
or the pathname of an image.

ColorList furthermore takes two named parameters,
a name and a list of tags.


def image_to_rgb(self, path, n=<NUM_LIT:10>):

from PIL import Image<EOL>img = Image.open(path)<EOL>p = img.getdata()<EOL>f = lambda p: choice(p)<EOL>for i in _range(n):<EOL><INDENT>rgba = f(p)<EOL>rgba = _list(rgba)<EOL>if len(rgba) == <NUM_LIT:3>:<EOL><INDENT>rgba.append(<NUM_LIT:255>)<EOL><DEDENT>r, g, b, a = [v / <NUM_LIT> for v in rgba]<EOL>clr = color(r, g, b, a, mode="<STR_LIT>")<EOL>self.append(clr)<EOL><DEDENT>

Returns a list of colors based on pixel values in the image.

The Core Image library must be present to determine pixel colors.
F. Albers: http://nodebox.net/code/index.php/shared_2007-06-11-11-37-05


matches = []<EOL>for clr in context:<EOL><INDENT>tags = context[clr]<EOL>for tag in tags:<EOL><INDENT>if tag.startswith(str)or str.startswith(tag):<EOL><INDENT>matches.append(clr)<EOL>break<EOL><DEDENT><DEDENT><DEDENT>matches = [color(name) for name in matches]<EOL>return matches<EOL>

Returns the colors that have the given word in their context.

        For example, the word "anger" appears
        in black, orange and red contexts,
        so the list will contain those three colors.


tags1 = None<EOL>for clr in self:<EOL><INDENT>overlap = []<EOL>if clr.is_black:<EOL><INDENT>name = "<STR_LIT>"<EOL><DEDENT>elif clr.is_white:<EOL><INDENT>name = "<STR_LIT>"<EOL><DEDENT>elif clr.is_grey:<EOL><INDENT>name = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>name = clr.nearest_hue(primary=True)<EOL><DEDENT>if name == "<STR_LIT>" and clr.brightness < <NUM_LIT>:<EOL><INDENT>name = "<STR_LIT>"<EOL><DEDENT>tags2 = context[name]<EOL>if tags1 is None:<EOL><INDENT>tags1 = tags2<EOL><DEDENT>else:<EOL><INDENT>for tag in tags2:<EOL><INDENT>if tag in tags1:<EOL><INDENT>if tag not in overlap:<EOL><INDENT>overlap.append(tag)<EOL><DEDENT><DEDENT><DEDENT>tags1 = overlap<EOL><DEDENT><DEDENT>overlap.sort()<EOL>return overlap<EOL>

Returns the intersection of each color's context.

Get the nearest named hue of each color,
and finds overlapping tags in each hue's colors.
For example, a list containing yellow, deeppink and olive
yields: femininity, friendship, happiness, joy.


return ColorList(<EOL>[color(clr.r, clr.g, clr.b, clr.a, mode="<STR_LIT>") for clr in self],<EOL>name=self.name,<EOL>tags=self.tags<EOL>)<EOL>


min, n = (<NUM_LIT:1.0>, <NUM_LIT:1.0>, <NUM_LIT:1.0>), <NUM_LIT><EOL>for clr in self:<EOL><INDENT>if clr.r + clr.g + clr.b < n:<EOL><INDENT>min, n = clr, clr.r + clr.g + clr.b<EOL><DEDENT><DEDENT>return min<EOL>

Returns the darkest color from the list.

Knowing the contrast between a light and a dark swatch
can help us decide how to display readable typography.


r, g, b, a = <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0><EOL>for clr in self:<EOL><INDENT>r += clr.r<EOL>g += clr.g<EOL>b += clr.b<EOL>a += clr.alpha<EOL><DEDENT>r /= len(self)<EOL>g /= len(self)<EOL>b /= len(self)<EOL>a /= len(self)<EOL>return color(r, g, b, a, mode="<STR_LIT>")<EOL>

Returns one average color for the colors in the list.


if len(self) == <NUM_LIT:0>: return ColorList()<EOL>root = self[<NUM_LIT:0>]<EOL>for clr in self[<NUM_LIT:1>:]:<EOL><INDENT>if clr.brightness < root.brightness:<EOL><INDENT>root = clr<EOL><DEDENT><DEDENT>stack = [clr for clr in self]<EOL>stack.remove(root)<EOL>sorted = [root]<EOL>while len(stack) > <NUM_LIT:1>:<EOL><INDENT>closest, distance = stack[<NUM_LIT:0>], stack[<NUM_LIT:0>].distance(sorted[-<NUM_LIT:1>])<EOL>for clr in stack[<NUM_LIT:1>:]:<EOL><INDENT>d = clr.distance(sorted[-<NUM_LIT:1>])<EOL>if d < distance:<EOL><INDENT>closest, distance = clr, d<EOL><DEDENT><DEDENT>stack.remove(closest)<EOL>sorted.append(closest)<EOL><DEDENT>sorted.append(stack[<NUM_LIT:0>])<EOL>if reversed: _list.reverse(sorted)<EOL>return ColorList(sorted)<EOL>

Returns a list with the smallest distance between two neighboring colors.
The algorithm has a factorial complexity so it may run slow.


def _sorted_copy(self, comparison, reversed=False):

sorted = self.copy()<EOL>_list.sort(sorted, comparison)<EOL>if reversed:<EOL><INDENT>_list.reverse(sorted)<EOL><DEDENT>return sorted<EOL>

Returns a sorted copy with the colors arranged according to the given comparison.


def sort(self, comparison="<STR_LIT>", reversed=False):

return getattr(self, "<STR_LIT>" + comparison)(reversed)<EOL>

Return a copy sorted by a given color attribute.

        Note that there is no "universal solution to sorting a list of colors,
        since colors need to be represented in 2 or 3 dimensions.


def cluster_sort(self, cmp1="<STR_LIT>", cmp2="<STR_LIT>", reversed=False, n=<NUM_LIT:12>):

sorted = self.sort(cmp1)<EOL>clusters = ColorList()<EOL>d = <NUM_LIT:1.0><EOL>i = <NUM_LIT:0><EOL>for j in _range(len(sorted)):<EOL><INDENT>if getattr(sorted[j], cmp1) < d:<EOL><INDENT>clusters.extend(sorted[i:j].sort(cmp2))<EOL>d -= <NUM_LIT:1.0> / n<EOL>i = j<EOL><DEDENT><DEDENT>clusters.extend(sorted[i:].sort(cmp2))<EOL>if reversed: _list.reverse(clusters)<EOL>return clusters<EOL>

Sorts the list by cmp1, then cuts it into n pieces which are sorted by cmp2.

If you want to cluster by hue, use n=12 (since there are 12 primary/secondary hues).
The resulting list will not contain n even slices:
n is used rather to slice up the cmp1 property of the colors,
e.g. cmp1=brightness and n=3 will cluster colors by brightness >= 0.66, 0.33, 0.0


colors = ColorList.copy(self)<EOL>_list.reverse(colors)<EOL>return colors<EOL>


def repeat(self, n=<NUM_LIT:2>, oscillate=False, callback=None):

colorlist = ColorList()<EOL>colors = ColorList.copy(self)<EOL>for i in _range(n):<EOL><INDENT>colorlist.extend(colors)<EOL>if oscillate: colors = colors.reverse()<EOL>if callback: colors = callback(colors)<EOL><DEDENT>return colorlist<EOL>

Returns a list that is a repetition of the given list.

When oscillate is True,
moves from the end back to the beginning,
and then from the beginning to the end, and so on.


for clr2 in self:<EOL><INDENT>if clr.r == clr2.r andclr.g == clr2.g andclr.b == clr2.b:<EOL><INDENT>return True<EOL><DEDENT><DEDENT>return False<EOL>

Returns True if clr's RGB values match a color in the list.


def swatch(self, x, y, w=<NUM_LIT>, h=<NUM_LIT>, padding=<NUM_LIT:0>, roundness=<NUM_LIT:0>):

for clr in self:<EOL><INDENT>clr.swatch(x, y, w, h, roundness)<EOL>y += h + padding<EOL><DEDENT>

Rectangle swatches for all the colors in the list.


sc = _ctx.stroke(<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>)<EOL>sw = _ctx.strokewidth(<NUM_LIT:0>)<EOL>_ctx.push()<EOL>_ctx.transform(_ctx.CORNER)<EOL>_ctx.translate(x, y)<EOL>for i in _range(r * <NUM_LIT:3>):<EOL><INDENT>clr = choice(self).copy()<EOL>clr.alpha -= <NUM_LIT:0.5> * random()<EOL>_ctx.fill(clr)<EOL>clr = choice(self)<EOL>_ctx.stroke(clr)<EOL>_ctx.strokewidth(<NUM_LIT:10> * random())<EOL>_ctx.rotate(<NUM_LIT> * random())<EOL>r2 = r * <NUM_LIT:0.5> * random()<EOL>_ctx.oval(r * random(), <NUM_LIT:0>, r2, r2)<EOL><DEDENT>_ctx.pop()<EOL>_ctx.strokewidth(sw)<EOL>if sc is None:<EOL><INDENT>_ctx.nostroke()<EOL><DEDENT>else:<EOL><INDENT>_ctx.stroke(sc)<EOL><DEDENT>

Fancy random ovals for all the colors in the list.


if len(colors) == <NUM_LIT:1>:<EOL><INDENT>if isinstance(colors[<NUM_LIT:0>], _list)or isinstance(colors[<NUM_LIT:0>], tuple):<EOL><INDENT>self._colors = _list(colors[<NUM_LIT:0>])<EOL><DEDENT>else:<EOL><INDENT>self._colors = [colors[<NUM_LIT:0>]]<EOL><DEDENT><DEDENT>else:<EOL><INDENT>self._colors = _list(colors)<EOL><DEDENT>self._colors = [color(clr) for clr in self._colors]<EOL>self._steps = <NUM_LIT:100><EOL>if kwargs.has_key("<STR_LIT>"):<EOL><INDENT>self._steps = kwargs["<STR_LIT>"]<EOL><DEDENT>if kwargs.has_key("<STR_LIT>"):<EOL><INDENT>self._steps = kwargs["<STR_LIT>"]<EOL><DEDENT>self._spread = <NUM_LIT:0.5><EOL>if kwargs.has_key("<STR_LIT>"):<EOL><INDENT>self._spread = kwargs["<STR_LIT>"]<EOL><DEDENT>self._cache()<EOL>

Creates a list of gradient colors based on a few given base colors.

The colors can be supplied as a list or tuple of colors,
or simply an enumeration of color parameters.

The steps named parameter defining how many colors are in the list.
The spread named parameter controls the midpoint of the gradient


def _interpolate(self, colors, n=<NUM_LIT:100>):

gradient = []<EOL>for i in _range(n):<EOL><INDENT>l = len(colors) - <NUM_LIT:1><EOL>x = int(<NUM_LIT:1.0> * i / n * l)<EOL>x = min(x + <NUM_LIT:0>, l)<EOL>y = min(x + <NUM_LIT:1>, l)<EOL>base = <NUM_LIT:1.0> * n / l * x<EOL>d = (i - base) / (<NUM_LIT:1.0> * n / l)<EOL>r = colors[x].r * (<NUM_LIT:1> - d) + colors[y].r * d<EOL>g = colors[x].g * (<NUM_LIT:1> - d) + colors[y].g * d<EOL>b = colors[x].b * (<NUM_LIT:1> - d) + colors[y].b * d<EOL>a = colors[x].a * (<NUM_LIT:1> - d) + colors[y].a * d<EOL>gradient.append(color(r, g, b, a, mode="<STR_LIT>"))<EOL><DEDENT>gradient.append(colors[-<NUM_LIT:1>])<EOL>return gradient<EOL>

Returns intermediary colors for given list of colors.


n = self.steps<EOL>if len(self._colors) == <NUM_LIT:1>:<EOL><INDENT>ColorList.__init__(self, [self._colors[<NUM_LIT:0>] for i in _range(n)])<EOL>return<EOL><DEDENT>colors = self._interpolate(self._colors, <NUM_LIT>)<EOL>left = colors[:len(colors) / <NUM_LIT:2>]<EOL>right = colors[len(colors) / <NUM_LIT:2>:]<EOL>left.append(right[<NUM_LIT:0>])<EOL>right.insert(<NUM_LIT:0>, left[-<NUM_LIT:1>])<EOL>gradient = self._interpolate(left, int(n * self.spread))[:-<NUM_LIT:1>]<EOL>gradient.extend(<EOL>self._interpolate(right, n - int(n * self.spread))[<NUM_LIT:1>:]<EOL>)<EOL>if self.spread > <NUM_LIT:1>: gradient = gradient[:n]<EOL>if self.spread < <NUM_LIT:0>: gradient = gradient[-n:]<EOL>ColorList.__init__(self, gradient)<EOL>

Populates the list with a number of gradient colors.

The list has Gradient.steps colors that interpolate between
the fixed base Gradient.colors.

The spread parameter controls the midpoint of the gradient,
you can shift it right and left. A separate gradient is
calculated for each half and then glued together.


if q is None:<EOL><INDENT>return self<EOL><DEDENT>candidate = None<EOL>if _favorites.data.has_key(q):<EOL><INDENT>candidate = q<EOL><DEDENT>for name, (tags, colors) in _favorites.data.iteritems():<EOL><INDENT>if q in tags:<EOL><INDENT>candidate = name<EOL><DEDENT><DEDENT>if candidate:<EOL><INDENT>tags, colors = _favorites.data[candidate]<EOL>colors = ColorList([color(r, g, b, a) for r, g, b, a in colors], name=candidate)<EOL>colors.tags = tags.split("<STR_LIT:U+0020>")<EOL>return colors<EOL><DEDENT>return None<EOL>

Returns the favorite colors list which name/tags matches q.


ColorList.__init__(self)<EOL>self.name = name<EOL>self.h = h<EOL>self.s = s<EOL>self.b = b<EOL>self.a = a<EOL>self.grayscale = grayscale<EOL>if not grayscale:<EOL><INDENT>self.black = ColorRange((<NUM_LIT:0>, <NUM_LIT:1>), <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, True, name)<EOL>self.white = ColorRange((<NUM_LIT:0>, <NUM_LIT:1>), <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>, True, name)<EOL><DEDENT>self.length = length<EOL>

A stateless list of colors whose HSB values are confined to a range.

Hue, saturation and brightness are confined to a (min, max) tuple,
or a list of (min, max) tuples for discontinuous ranges, or to a single value.
This way you can describe concepts such as "light", "dark", etc.

With stateless we mean that you are never sure which colors are
in the ColorRange, different colors that fall within the ranges
are returned each time when calling color() or colors().

ColorRange has all the ColorList transformations (such as darken()),
these return ColorList objects. It's like a snapshot of the original
stateless ColorRange.


def copy(self, clr=None, d=<NUM_LIT:0.0>):

cr = ColorRange()<EOL>cr.name = self.name<EOL>cr.h = deepcopy(self.h)<EOL>cr.s = deepcopy(self.s)<EOL>cr.b = deepcopy(self.b)<EOL>cr.a = deepcopy(self.a)<EOL>cr.grayscale = self.grayscale<EOL>if not self.grayscale:<EOL><INDENT>cr.black = self.black.copy()<EOL>cr.white = self.white.copy()<EOL><DEDENT>if clr != None:<EOL><INDENT>cr.h, cr.a = clr.h + d * (random() * <NUM_LIT:2> - <NUM_LIT:1>), clr.a<EOL><DEDENT>return cr<EOL>

Returns a copy of the range.

Optionally, supply a color to get a range copy
limited to the hue of that color.


<EOL>if clr != None and not isinstance(clr, Color):<EOL><INDENT>clr = color(clr)<EOL><DEDENT>if clr != None and not self.grayscale:<EOL><INDENT>if clr.is_black: return self.black.color(clr, d)<EOL>if clr.is_white: return self.white.color(clr, d)<EOL>if clr.is_grey: return choice(<EOL>(self.black.color(clr, d), self.white.color(clr, d))<EOL>)<EOL><DEDENT>h, s, b, a = self.h, self.s, self.b, self.a<EOL>if clr != None:<EOL><INDENT>h, a = clr.h + d * (random() * <NUM_LIT:2> - <NUM_LIT:1>), clr.a<EOL><DEDENT>hsba = []<EOL>for v in [h, s, b, a]:<EOL><INDENT>if isinstance(v, _list):<EOL><INDENT>min, max = choice(v)<EOL><DEDENT>elif isinstance(v, tuple):<EOL><INDENT>min, max = v<EOL><DEDENT>else:<EOL><INDENT>min, max = v, v<EOL><DEDENT>hsba.append(min + (max - min) * random())<EOL><DEDENT>h, s, b, a = hsba<EOL>return color(h, s, b, a, mode="<STR_LIT>")<EOL>

Returns a color with random values in the defined h, s b, a ranges.

If a color is given, use that color's hue and alpha,
and generate its saturation and brightness from the shade.
The hue is varied with the given d.

In this way you could have a "warm" color range
that returns all kinds of warm colors.
When a red color is given as parameter it would generate
all kinds of warm red colors.


if not isinstance(clr, Color):<EOL><INDENT>return False<EOL><DEDENT>if not isinstance(clr, _list):<EOL><INDENT>clr = [clr]<EOL><DEDENT>for clr in clr:<EOL><INDENT>if clr.is_grey and not self.grayscale:<EOL><INDENT>return (self.black.contains(clr) orself.white.contains(clr))<EOL><DEDENT>for r, v in [(self.h, clr.h), (self.s, clr.s), (self.b, clr.brightness), (self.a, clr.a)]:<EOL><INDENT>if isinstance(r, _list):<EOL><INDENT>pass<EOL><DEDENT>elif isinstance(r, tuple):<EOL><INDENT>r = [r]<EOL><DEDENT>else:<EOL><INDENT>r = [(r, r)]<EOL><DEDENT>for min, max in r:<EOL><INDENT>if not (min <= v <= max):<EOL><INDENT>return False<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return True<EOL>

Returns True if the given color is part of this color range.

Check whether each h, s, b, a component of the color
falls within the defined range for that component.

If the given color is grayscale,
checks against the definitions for black and white.


<EOL>if isinstance(colorrange, Color):<EOL><INDENT>colorrange = ColorList(colorrange)<EOL><DEDENT>if isinstance(colorrange, ColorList)and not isinstance(colorrange, ColorRange):<EOL><INDENT>colorrange = ColorRange([(clr.h, clr.h) for clr in colorrange], [], [])<EOL><DEDENT>hsba = [[], [], [], []]<EOL>for r in [self, colorrange]:<EOL><INDENT>for i in _range(<NUM_LIT:4>):<EOL><INDENT>v = [r.h, r.s, r.b, r.a][i]<EOL>if isinstance(v, _list):<EOL><INDENT>hsba[i].extend(v)<EOL><DEDENT>elif isinstance(v, tuple):<EOL><INDENT>hsba[i].append(v)<EOL><DEDENT>else:<EOL><INDENT>hsba[i].append((v, v))<EOL><DEDENT><DEDENT><DEDENT>r = ColorRange(*hsba)<EOL>return r<EOL>

Combines two ColorRange objects into one.

For example, if you merge a dark green range and a light red range,
you get a range returning dark and light variations of green and red.


def __init__(self, name="<STR_LIT>", ranges=[], top=<NUM_LIT:5>, cache=DEFAULT_CACHE, blue="<STR_LIT>", guess=False, length=<NUM_LIT:100>):

self.name = name<EOL>self.ranges = []<EOL>self.cache = cache<EOL>self.top = top<EOL>self.tags = []<EOL>self.blue = blue<EOL>self.guess = False<EOL>self.length = <NUM_LIT:100><EOL>self.group_swatches = False<EOL>path = os.path.join(self.cache, self.name + "<STR_LIT>")<EOL>if os.path.exists(path):<EOL><INDENT>self._load(self.top, self.blue)<EOL><DEDENT>elif name and zipfile.is_zipfile(os.path.join(self.cache + '<STR_LIT>')):<EOL><INDENT>archive = self.cache + '<STR_LIT>'<EOL>zf = zipfile.ZipFile(archive, '<STR_LIT:r>')<EOL>zpath = os.path.join(os.path.basename(self.cache), self.name + "<STR_LIT>")<EOL>try:<EOL><INDENT>zi = zf.getinfo(zpath)<EOL>self.cache = zi<EOL>self._load(self.top, self.blue, archive=zf, member=zi.filename)<EOL><DEDENT>except KeyError:<EOL><INDENT>for fn in zf.namelist():<EOL><INDENT>if os.path.basename(fn) == self.name + '<STR_LIT>':<EOL><INDENT>zi = zf.getinfo(fn)<EOL>self.cache = zi<EOL>self._load(self.top, self.blue, archive=zf, member=zi.filename)<EOL>break<EOL><DEDENT><DEDENT><DEDENT><DEDENT>else:<EOL><INDENT>a = aggregated(self.cache)<EOL>for key in a:<EOL><INDENT>if self.name != "<STR_LIT>" and self.name in a[key]:<EOL><INDENT>self.cache = os.path.join(self.cache, key)<EOL>self._load(self.top, self.blue)<EOL>self.tags.append(key.replace("<STR_LIT:_>", "<STR_LIT:U+0020>"))<EOL>self.group_swatches = True<EOL>break<EOL><DEDENT><DEDENT><DEDENT>if len(ranges) > <NUM_LIT:0>:<EOL><INDENT>self.ranges = ranges<EOL><DEDENT>if len(self.ranges) == <NUM_LIT:0> and guess:<EOL><INDENT>a = aggregated(self.cache)<EOL>for key in a:<EOL><INDENT>m = difflib.get_close_matches(self.name, a[key], cutoff=<NUM_LIT>)<EOL>if len(m) > <NUM_LIT:0>:<EOL><INDENT>self.name = m[<NUM_LIT:0>]<EOL>self.cache = os.path.join(self.cache, key)<EOL>self._load(top, blue)<EOL>self.tags.append(key.replace("<STR_LIT:_>", "<STR_LIT:U+0020>"))<EOL>self.group_swatches = True<EOL>self.guess = True<EOL>break<EOL><DEDENT><DEDENT><DEDENT>if self.name != "<STR_LIT>" and len(self.ranges) == <NUM_LIT:0>:<EOL><INDENT>raise ColorThemeNotFound<EOL><DEDENT>

A set of weighted ranges linked to colors.

A ColorTheme is a set of allowed colors (e.g. red, black)
and ranges (e.g. dark, intense) for these colors.
These are supplied as lists of (color, range, weight) tuples.
Ranges with a greater weight will occur more in the combined range.

A ColorTheme is expected to have a name,
so it can be stored and retrieved in the XML cache.

The blue parameter denotes a color correction.
Since most web aggregated results will yield "blue" instead of "azure" or "cyan",
we may never see these colors (e.g. azure beach will not propagate).
So instead of true blue we pass "dodgerblue", which will yield more all-round shades of blue.
To ignore this, set blue="blue".


grouped = {}<EOL>weights = []<EOL>for clr, rng, weight in self.ranges:<EOL><INDENT>h = clr.nearest_hue(primary=False)<EOL>if grouped.has_key(h):<EOL><INDENT>ranges, total_weight = grouped[h]<EOL>ranges.append((clr, rng, weight))<EOL>total_weight += weight<EOL>grouped[h] = (ranges, total_weight)<EOL><DEDENT>else:<EOL><INDENT>grouped[h] = ([(clr, rng, weight)], weight)<EOL><DEDENT><DEDENT>s = <NUM_LIT:1.0> * sum([w for r, w in grouped.values()])<EOL>grouped = [(grouped[h][<NUM_LIT:1>], grouped[h][<NUM_LIT:1>] / s, h, grouped[h][<NUM_LIT:0>]) for h in grouped]<EOL>grouped.sort()<EOL>grouped.reverse()<EOL>return grouped<EOL>

Returns a list of (hue, ranges, total weight, normalized total weight)-tuples.

ColorTheme is made up out of (color, range, weight) tuples.
For consistency with XML-output in the old Prism format
(i.e. <color>s made up of <shade>s) we need a group
weight per different hue.

The same is true for the swatch() draw method.
Hues are grouped as a single unit (e.g. dark red, intense red, weak red)
after which the dimensions (rows/columns) is determined.


grouped = self._weight_by_hue()<EOL>xml = "<STR_LIT>" + self.name + "<STR_LIT>" + "<STR_LIT:U+002CU+0020>".join(self.tags) + "<STR_LIT>"<EOL>for total_weight, normalized_weight, hue, ranges in grouped:<EOL><INDENT>if hue == self.blue: hue = "<STR_LIT>"<EOL>clr = color(hue)<EOL>xml += "<STR_LIT>" + clr.name + "<STR_LIT>" + str(normalized_weight) + "<STR_LIT>"<EOL>xml += "<STR_LIT>" + str(clr.r) + "<STR_LIT>" + str(clr.g) + "<STR_LIT>"<EOL>xml += "<STR_LIT>" + str(clr.b) + "<STR_LIT>" + str(clr.a) + "<STR_LIT>"<EOL>for clr, rng, wgt in ranges:<EOL><INDENT>xml += "<STR_LIT>" + str(rng) + "<STR_LIT>" + str(wgt / total_weight) + "<STR_LIT>"<EOL><DEDENT>xml = xml.rstrip("<STR_LIT:U+0020>") + "<STR_LIT>"<EOL><DEDENT>xml += "<STR_LIT>"<EOL>return xml<EOL>

Returns the color information as XML.

The XML has the following structure:
<colors query="">
    <color name="" weight="" />
        <rgb r="" g="" b="" />
        <shade name="" weight="" />
    </color>
</colors>

Notice that ranges are stored by name and retrieved in the _load()
method with the shade() command - and are thus expected to be
shades (e.g. intense, warm, ...) unless the shade() command would
return any custom ranges as well. This can be done by appending custom
ranges to the shades list.


if not os.path.exists(self.cache):<EOL><INDENT>os.makedirs(self.cache)<EOL><DEDENT>path = os.path.join(self.cache, self.name + "<STR_LIT>")<EOL>f = open(path, "<STR_LIT:w>")<EOL>f.write(self.xml)<EOL>f.close()<EOL>

Saves the color information in the cache as XML.


def _load(self, top=<NUM_LIT:5>, blue="<STR_LIT>", archive=None, member=None):

if archive is None:<EOL><INDENT>path = os.path.join(self.cache, self.name + "<STR_LIT>")<EOL>xml = open(path).read()<EOL><DEDENT>else:<EOL><INDENT>assert member is not None<EOL>xml = archive.read(member)<EOL><DEDENT>dom = parseString(xml).documentElement<EOL>attr = lambda e, a: e.attributes[a].value<EOL>for e in dom.getElementsByTagName("<STR_LIT>")[:top]:<EOL><INDENT>w = float(attr(e, "<STR_LIT>"))<EOL>try:<EOL><INDENT>rgb = e.getElementsByTagName("<STR_LIT>")[<NUM_LIT:0>]<EOL>clr = color(<EOL>float(attr(rgb, "<STR_LIT:r>")),<EOL>float(attr(rgb, "<STR_LIT:g>")),<EOL>float(attr(rgb, "<STR_LIT:b>")),<EOL>float(attr(rgb, "<STR_LIT:a>")),<EOL>mode="<STR_LIT>"<EOL>)<EOL>try:<EOL><INDENT>clr.name = attr(e, "<STR_LIT:name>")<EOL>if clr.name == "<STR_LIT>": clr = color(blue)<EOL><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT><DEDENT>except:<EOL><INDENT>name = attr(e, "<STR_LIT:name>")<EOL>if name == "<STR_LIT>": name = blue<EOL>clr = color(name)<EOL><DEDENT>for s in e.getElementsByTagName("<STR_LIT>"):<EOL><INDENT>self.ranges.append((<EOL>clr,<EOL>shade(attr(s, "<STR_LIT:name>")),<EOL>w * float(attr(s, "<STR_LIT>"))<EOL>))<EOL><DEDENT><DEDENT>

Loads a theme from aggregated web data.

The data must be old-style Prism XML: <color>s consisting of <shade>s.
Colors named "blue" will be overridden with the blue parameter.

archive can be a file like object (e.g. a ZipFile)
and will be used along with 'member' if specified.


s = sum([w for clr, rng, w in self.ranges])<EOL>r = random()<EOL>for clr, rng, weight in self.ranges:<EOL><INDENT>if weight / s >= r: break<EOL>r -= weight / s<EOL><DEDENT>return rng(clr, d)<EOL>

Returns a random color within the theme.

Fetches a random range (the weight is taken into account,
so ranges with a bigger weight have a higher chance of propagating)
and hues it with the associated color.


def colors(self, n=<NUM_LIT:10>, d=<NUM_LIT>):

s = sum([w for clr, rng, w in self.ranges])<EOL>colors = colorlist()<EOL>for i in _range(n):<EOL><INDENT>r = random()<EOL>for clr, rng, weight in self.ranges:<EOL><INDENT>if weight / s >= r: break<EOL>r -= weight / s<EOL><DEDENT>colors.append(rng(clr, d))<EOL><DEDENT>return colors<EOL>

Returns a number of random colors from the theme.


def recombine(self, other, d=<NUM_LIT>):

a, b = self, other<EOL>d1 = max(<NUM_LIT:0>, min(d, <NUM_LIT:1>))<EOL>d2 = d1<EOL>c = ColorTheme(<EOL>name=a.name[:int(len(a.name) * d1)] +<EOL>b.name[int(len(b.name) * d2):],<EOL>ranges=a.ranges[:int(len(a.ranges) * d1)] +<EOL>b.ranges[int(len(b.ranges) * d2):],<EOL>top=a.top,<EOL>cache=os.path.join(DEFAULT_CACHE, "<STR_LIT>"),<EOL>blue=a.blue,<EOL>length=a.length * d1 + b.length * d2<EOL>)<EOL>c.tags = a.tags[:int(len(a.tags) * d1)]<EOL>c.tags += b.tags[int(len(b.tags) * d2):]<EOL>return c<EOL>

Genetic recombination of two themes using cut and splice technique.


def swatch(self, x, y, w=<NUM_LIT>, h=<NUM_LIT>, padding=<NUM_LIT:4>, roundness=<NUM_LIT:0>, n=<NUM_LIT:12>, d=<NUM_LIT>, grouped=None):

if grouped is None:  <EOL><INDENT>grouped = self.group_swatches<EOL><DEDENT>if not grouped:<EOL><INDENT>s = sum([wgt for clr, rng, wgt in self.ranges])<EOL>for clr, rng, wgt in self.ranges:<EOL><INDENT>cols = max(<NUM_LIT:1>, int(wgt / s * n))<EOL>for i in _range(cols):<EOL><INDENT>rng.colors(clr, n=n, d=d).swatch(x, y, w, h, padding=padding, roundness=roundness)<EOL>x += w + padding<EOL><DEDENT><DEDENT>return x, y + n * (h + padding)<EOL><DEDENT>grouped = self._weight_by_hue()<EOL>for total_weight, normalized_weight, hue, ranges in grouped:<EOL><INDENT>dy = y<EOL>rc = <NUM_LIT:0><EOL>for clr, rng, weight in ranges:<EOL><INDENT>dx = x<EOL>cols = int(normalized_weight * n)<EOL>cols = max(<NUM_LIT:1>, min(cols, n - len(grouped)))<EOL>if clr.name == "<STR_LIT>": rng = rng.black<EOL>if clr.name == "<STR_LIT>": rng = rng.white<EOL>for i in _range(cols):<EOL><INDENT>rows = int(weight / total_weight * n)<EOL>rows = max(<NUM_LIT:1>, rows)<EOL>if (clr, rng, weight) == ranges[-<NUM_LIT:1>] and rc + rows < n: rows += <NUM_LIT:1><EOL>rng.colors(clr, n=rows, d=d).swatch(dx, dy, w, h, padding=padding, roundness=roundness)<EOL>dx += w + padding<EOL><DEDENT>dy += (w + padding) * rows  <EOL>rc = rows<EOL><DEDENT>x += (w + padding) * cols + padding<EOL><DEDENT>return x, dy<EOL>

Draws a weighted swatch with approximately n columns and rows.

When the grouped parameter is True, colors are grouped in blocks of the same hue
(also see the _weight_by_hue() method).


def __init__(self, dx=<NUM_LIT:10>, dy=<NUM_LIT:10>, alpha=<NUM_LIT>, blur=<NUM_LIT>, clr=None):

Grob.__init__(self, _ctx)<EOL>if clr is None:<EOL><INDENT>clr = color(<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:0>, alpha, mode="<STR_LIT>")<EOL><DEDENT>self.dx = dx<EOL>self.dy = dy<EOL>self.blur = blur<EOL>self.clr = clr.copy()<EOL>self.clr.alpha = alpha<EOL>global _shadow<EOL>_shadow = self<EOL>

Sets the dropshadow for all onscreen elements.

Both the fill and stroke of a path get a dropshadow.

TODO - Implement shadow, could work as a postprocessing effect


def __init__(self, path, clr1, clr2, type="<STR_LIT>", dx=<NUM_LIT:0>, dy=<NUM_LIT:0>, spread=<NUM_LIT:1.0>, angle=<NUM_LIT:0>, alpha=<NUM_LIT:1.0>):

raise NotImplementedError("<STR_LIT>")<EOL>self.path = path<EOL>self.path.inheritFromContext()<EOL>self.path.fillcolor = colorlist(clr1, clr2).average<EOL>self.path.fillcolor.alpha *= alpha<EOL>self.clr1 = clr1<EOL>self.clr2 = clr2<EOL>self.type = type<EOL>self.dx = dx<EOL>self.dy = dy<EOL>self.spread = spread<EOL>self.angle = angle<EOL>_ctx.canvas.append(self)<EOL>if _shadow:<EOL><INDENT>self._shadow_alpha = _shadow.clr.alpha<EOL><DEDENT>else:<EOL><INDENT>self._shadow_alpha = <NUM_LIT:0><EOL><DEDENT>

Fills a path with a smooth gradient between two colors.

Creates a Core Image gradient and clips it to the given path.
The type can be radial or linear.
The spread is the distance between the two colors (0.0-1.0 or absolute).
The angle is useful for linear gradients, setting it to 90 degrees
creates a horizontal instead of a vertical gradient.

The gradient is in RGB color.
If shadows are being used, it is rendered in a clipping area
with a background that is the average of the two gradient colors
(we need a fill to render a shadow).
You can tweak this background's opacity with the alpha parameter.


def parse(svg, cached=False, _copy=True):

if not cached:<EOL><INDENT>dom = parser.parseString(svg)<EOL>paths = parse_node(dom, [])<EOL><DEDENT>else:<EOL><INDENT>id = _cache.id(svg)<EOL>if not _cache.has_key(id):<EOL><INDENT>dom = parser.parseString(svg)<EOL>_cache.save(id, parse_node(dom, []))<EOL><DEDENT>paths = _cache.load(id, _copy)<EOL><DEDENT>return paths<EOL>

Returns cached copies unless otherwise specified.


def get_attribute(element, attribute, default=<NUM_LIT:0>):

a = element.getAttribute(attribute)<EOL>if a == "<STR_LIT>": <EOL><INDENT>return default<EOL><DEDENT>return a<EOL>

Returns XML element's attribute, or default if none.


def parse_node(node, paths=[], ignore=["<STR_LIT>"]):

<EOL>if node.nodeType == node.ELEMENT_NODE and node.tagName in ignore: <EOL><INDENT>return []<EOL><DEDENT>if node.hasChildNodes():<EOL><INDENT>for child in node.childNodes:<EOL><INDENT>paths = parse_node(child, paths)<EOL><DEDENT><DEDENT>if node.nodeType == node.ELEMENT_NODE:<EOL><INDENT>if node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_line(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_rect(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_circle(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_oval(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_polygon(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT>":<EOL><INDENT>paths.append(parse_polygon(node))<EOL><DEDENT>elif node.tagName == "<STR_LIT:path>":<EOL><INDENT>paths.append(parse_path(node))<EOL><DEDENT>if node.tagName in ("<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT:path>"):<EOL><INDENT>paths[-<NUM_LIT:1>] = parse_transform(node, paths[-<NUM_LIT:1>])<EOL>paths[-<NUM_LIT:1>] = add_color_info(node, paths[-<NUM_LIT:1>])<EOL><DEDENT><DEDENT>return paths<EOL>

Recurse the node tree and find drawable tags.

    Recures all the children in the node.
    If a child is something we can draw,
    a line, rect, oval or path,
    parse it to a PathElement drawable with drawpath()


t = get_attribute(e, "<STR_LIT>", default="<STR_LIT>")<EOL>for mode in ("<STR_LIT>", "<STR_LIT>"):<EOL><INDENT>if t.startswith(mode):<EOL><INDENT>v = t.replace(mode, "<STR_LIT>").lstrip("<STR_LIT:(>").rstrip("<STR_LIT:)>")<EOL>v = v.replace("<STR_LIT:U+002CU+0020>", "<STR_LIT:U+002C>").replace("<STR_LIT:U+0020>", "<STR_LIT:U+002C>")<EOL>v = [float(x) for x in v.split("<STR_LIT:U+002C>")]<EOL>from nodebox.graphics import Transform<EOL>t = Transform()            <EOL>if mode == "<STR_LIT>":<EOL><INDENT>t._set_matrix(v)<EOL><DEDENT>elif mode == "<STR_LIT>":<EOL><INDENT>t.translate(*v)<EOL><DEDENT>path = t.transformBezierPath(path)<EOL>break<EOL><DEDENT><DEDENT>e = e.parentNode<EOL>if e and e.tagName == "<STR_LIT:g>":<EOL><INDENT>path = parse_transform(e, path)<EOL><DEDENT>return path<EOL>

Transform the path according to a defined matrix.

    Attempts to extract a transform="matrix()|translate()" attribute.
    Transforms the path accordingly.


_ctx.colormode(RGB, <NUM_LIT:1.0>)<EOL>def _color(hex, alpha=<NUM_LIT:1.0>):<EOL><INDENT>if hex == "<STR_LIT:none>": return None<EOL>n = int(hex[<NUM_LIT:1>:],<NUM_LIT:16>)<EOL>r = (n>><NUM_LIT:16>)&<NUM_LIT><EOL>g = (n>><NUM_LIT:8>)&<NUM_LIT><EOL>b = n&<NUM_LIT><EOL>return _ctx.color(r/<NUM_LIT>, g/<NUM_LIT>, b/<NUM_LIT>, alpha)<EOL><DEDENT>path.fill = (<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:0>)<EOL>path.stroke = (<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:0>,<NUM_LIT:0>)<EOL>path.strokewidth = <NUM_LIT:0><EOL>alpha = get_attribute(e, "<STR_LIT>", default="<STR_LIT>")<EOL>if alpha == "<STR_LIT>":<EOL><INDENT>alpha = <NUM_LIT:1.0><EOL><DEDENT>else:<EOL><INDENT>alpha = float(alpha)<EOL><DEDENT>try: path.fill = _color(get_attribute(e, "<STR_LIT>", default="<STR_LIT>"), alpha)<EOL>except: <EOL><INDENT>pass<EOL><DEDENT>try: path.stroke = _color(get_attribute(e, "<STR_LIT>", default="<STR_LIT:none>"), alpha)<EOL>except: <EOL><INDENT>pass<EOL><DEDENT>try: path.strokewidth = float(get_attribute(e, "<STR_LIT>", default="<STR_LIT:1>"))<EOL>except: <EOL><INDENT>pass<EOL><DEDENT>style = get_attribute(e, "<STR_LIT>", default="<STR_LIT>").split("<STR_LIT:;>")<EOL>for s in style:<EOL><INDENT>try:<EOL><INDENT>if s.startswith("<STR_LIT>"):<EOL><INDENT>path.fill = _color(s.replace("<STR_LIT>", "<STR_LIT>"))<EOL><DEDENT>elif s.startswith("<STR_LIT>"):<EOL><INDENT>path.stroke = _color(s.replace("<STR_LIT>", "<STR_LIT>"))<EOL><DEDENT>elif s.startswith("<STR_LIT>"):<EOL><INDENT>path.strokewidth = float(s.replace("<STR_LIT>", "<STR_LIT>"))<EOL><DEDENT><DEDENT>except:<EOL><INDENT>pass    <EOL><DEDENT><DEDENT>path.closed = False<EOL>if path[<NUM_LIT:0>].x == path[len(path)-<NUM_LIT:1>].x andpath[<NUM_LIT:0>].y == path[len(path)-<NUM_LIT:1>].y: <EOL><INDENT>path.closed = True<EOL><DEDENT>for i in range(<NUM_LIT:1>,-<NUM_LIT:1>):<EOL><INDENT>if path[i].cmd == MOVETO:<EOL><INDENT>path.closed = False<EOL><DEDENT><DEDENT>return path<EOL>

Expand the path with color information.

    Attempts to extract fill and stroke colors
    from the element and adds it to path attributes.


def bezier_arc(x1, y1, x2, y2, start_angle=<NUM_LIT:0>, extent=<NUM_LIT>):

x1,y1, x2,y2 = min(x1,x2), max(y1,y2), max(x1,x2), min(y1,y2)<EOL>if abs(extent) <= <NUM_LIT>:<EOL><INDENT>frag_angle = float(extent)<EOL>nfrag = <NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>nfrag = int(ceil(abs(extent)/<NUM_LIT>))<EOL>if nfrag == <NUM_LIT:0>:<EOL><INDENT>warnings.warn('<STR_LIT>' % extent)<EOL>return []<EOL><DEDENT>frag_angle = float(extent) / nfrag<EOL><DEDENT>x_cen = (x1+x2)/<NUM_LIT><EOL>y_cen = (y1+y2)/<NUM_LIT><EOL>rx = (x2-x1)/<NUM_LIT><EOL>ry = (y2-y1)/<NUM_LIT><EOL>half_angle = radians(frag_angle) / <NUM_LIT:2><EOL>kappa = abs(<NUM_LIT> / <NUM_LIT> * (<NUM_LIT:1.> - cos(half_angle)) / sin(half_angle))<EOL>if frag_angle < <NUM_LIT:0>:<EOL><INDENT>sign = -<NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>sign = <NUM_LIT:1><EOL><DEDENT>point_list = []<EOL>for i in range(nfrag):<EOL><INDENT>theta0 = radians(start_angle + i*frag_angle)<EOL>theta1 = radians(start_angle + (i+<NUM_LIT:1>)*frag_angle)<EOL>c0 = cos(theta0)<EOL>c1 = cos(theta1)<EOL>s0 = sin(theta0)<EOL>s1 = sin(theta1)<EOL>if frag_angle > <NUM_LIT:0>:<EOL><INDENT>signed_kappa = -kappa<EOL><DEDENT>else:<EOL><INDENT>signed_kappa = kappa<EOL><DEDENT>point_list.append((x_cen + rx * c0,<EOL>y_cen - ry * s0,<EOL>x_cen + rx * (c0 + signed_kappa * s0),<EOL>y_cen - ry * (s0 - signed_kappa * c0),<EOL>x_cen + rx * (c1 - signed_kappa * s1),<EOL>y_cen - ry * (s1 + signed_kappa * c1),<EOL>x_cen + rx * c1,<EOL>y_cen - ry * s1))<EOL><DEDENT>return point_list<EOL>

Compute a cubic Bezier approximation of an elliptical arc.

    (x1, y1) and (x2, y2) are the corners of the enclosing rectangle.
    The coordinate system has coordinates that increase to the right and down.
    Angles, measured in degress, start with 0 to the right (the positive X axis)
    and increase counter-clockwise.
    The arc extends from start_angle to start_angle+extent.
    I.e. start_angle=0 and extent=180 yields an openside-down semi-circle.

    The resulting coordinates are of the form (x1,y1, x2,y2, x3,y3, x4,y4)
    such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
    (x3, y3) as their respective Bezier control points.


sign = <NUM_LIT:1.0><EOL>usign = (x1*y2 - y1*x2)<EOL>if usign < <NUM_LIT:0>:<EOL><INDENT>sign = -<NUM_LIT:1.0><EOL><DEDENT>num = x1*x2 + y1*y2<EOL>den = hypot(x1,y1) * hypot(x2,y2)<EOL>ratio = min(max(num/den, -<NUM_LIT:1.0>), <NUM_LIT:1.0>)<EOL>return sign * degrees(acos(ratio))<EOL>

The angle in degrees between two vectors.


def transform_from_local(xp, yp, cphi, sphi, mx, my):

x = xp * cphi - yp * sphi + mx<EOL>y = xp * sphi + yp * cphi + my<EOL>return (x,y)<EOL>

Transform from the local frame to absolute space.


def elliptical_arc_to(x1, y1, rx, ry, phi, large_arc_flag, sweep_flag, x2, y2):

<EOL>rx = abs(rx)<EOL>ry = abs(ry)<EOL>phi = phi % <NUM_LIT><EOL>if x1==x2 and y1==y2:<EOL><INDENT>return []<EOL><DEDENT>if rx == <NUM_LIT:0> or ry == <NUM_LIT:0>:<EOL><INDENT>return [(x2,y2)]<EOL><DEDENT>rphi = radians(phi)<EOL>cphi = cos(rphi)<EOL>sphi = sin(rphi)<EOL>dx = <NUM_LIT:0.5>*(x1 - x2)<EOL>dy = <NUM_LIT:0.5>*(y1 - y2)<EOL>x1p =  cphi * dx + sphi * dy<EOL>y1p = -sphi * dx + cphi * dy<EOL>lam = (x1p/rx)**<NUM_LIT:2> + (y1p/ry)**<NUM_LIT:2><EOL>if lam > <NUM_LIT:1.0>:<EOL><INDENT>scale = sqrt(lam)<EOL>rx *= scale<EOL>ry *= scale<EOL><DEDENT>num = max((rx*ry)**<NUM_LIT:2> - (rx*y1p)**<NUM_LIT:2> - (ry*x1p)**<NUM_LIT:2>, <NUM_LIT:0.0>)<EOL>den = ((rx*y1p)**<NUM_LIT:2> + (ry*x1p)**<NUM_LIT:2>)<EOL>a = sqrt(num / den)<EOL>cxp = a * rx*y1p/ry<EOL>cyp = -a * ry*x1p/rx<EOL>if large_arc_flag == sweep_flag:<EOL><INDENT>cxp = -cxp<EOL>cyp = -cyp<EOL><DEDENT>mx = <NUM_LIT:0.5>*(x1+x2)<EOL>my = <NUM_LIT:0.5>*(y1+y2)<EOL>dx = (x1p-cxp) / rx<EOL>dy = (y1p-cyp) / ry<EOL>dx2 = (-x1p-cxp) / rx<EOL>dy2 = (-y1p-cyp) / ry<EOL>theta1 = angle(<NUM_LIT:1>,<NUM_LIT:0>,dx,dy)<EOL>dtheta = angle(dx,dy,dx2,dy2)<EOL>if not sweep_flag and dtheta > <NUM_LIT:0>:<EOL><INDENT>dtheta -= <NUM_LIT><EOL><DEDENT>elif sweep_flag and dtheta < <NUM_LIT:0>:<EOL><INDENT>dtheta += <NUM_LIT><EOL><DEDENT>p = []<EOL>control_points = bezier_arc(cxp-rx,cyp-ry,cxp+rx,cyp+ry, theta1, dtheta)<EOL>for x1p,y1p, x2p,y2p, x3p,y3p, x4p,y4p in control_points:<EOL><INDENT>p.append((<EOL>transform_from_local(x2p,y2p,cphi,sphi,mx,my) +<EOL>transform_from_local(x3p,y3p,cphi,sphi,mx,my) +<EOL>transform_from_local(x4p,y4p,cphi,sphi,mx,my)<EOL>))<EOL><DEDENT>return p<EOL>

An elliptical arc approximated with Bezier curves or a line segment.
Algorithm taken from the SVG 1.1 Implementation Notes:
http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes


vx = vy = vz = <NUM_LIT:0><EOL>for b in self.boids:<EOL><INDENT>if b != self:<EOL><INDENT>vx, vy, vz = vx+b.x, vy+b.y, vz+b.z<EOL><DEDENT><DEDENT>n = len(self.boids)-<NUM_LIT:1><EOL>vx, vy, vz = vx/n, vy/n, vz/n<EOL>return (vx-self.x)/d, (vy-self.y)/d, (vz-self.z)/d<EOL>

Boids move towards the flock's centre of mass.

        The centre of mass is the average position of all boids,
        not including itself (the "perceived centre").


vx = vy = vz = <NUM_LIT:0><EOL>for b in self.boids:<EOL><INDENT>if b != self:<EOL><INDENT>if abs(self.x-b.x) < r: vx += (self.x-b.x)<EOL>if abs(self.y-b.y) < r: vy += (self.y-b.y)<EOL>if abs(self.z-b.z) < r: vz += (self.z-b.z)<EOL><DEDENT><DEDENT>return vx, vy, vz<EOL>

Boids keep a small distance from other boids.

        Ensures that boids don't collide into each other,
        in a smoothly accelerated motion.


vx = vy = vz = <NUM_LIT:0><EOL>for b in self.boids:<EOL><INDENT>if b != self:<EOL><INDENT>vx, vy, vz = vx+b.vx, vy+b.vy, vz+b.vz<EOL><DEDENT><DEDENT>n = len(self.boids)-<NUM_LIT:1><EOL>vx, vy, vz = vx/n, vy/n, vz/n<EOL>return (vx-self.vx)/d, (vy-self.vy)/d, (vz-self.vz)/d<EOL>


if abs(self.vx) > max: <EOL><INDENT>self.vx = self.vx/abs(self.vx)*max<EOL><DEDENT>if abs(self.vy) > max: <EOL><INDENT>self.vy = self.vy/abs(self.vy)*max<EOL><DEDENT>if abs(self.vz) > max: <EOL><INDENT>self.vz = self.vz/abs(self.vz)*max<EOL><DEDENT>

The speed limit for a boid.

        Boids can momentarily go very fast,
        something that is impossible for real animals.


from math import atan, pi, degrees<EOL>a = degrees(atan(self.vy/self.vx)) + <NUM_LIT><EOL>if self.vx < <NUM_LIT:0>: a += <NUM_LIT><EOL>return a<EOL>

Returns the angle towards which the boid is steering.


return (x-self.x)/d, (y-self.y)/d, (z-self.z)/d<EOL>


dx = self.w * <NUM_LIT:0.1><EOL>dy = self.h * <NUM_LIT:0.1> <EOL>for b in self:<EOL><INDENT>if b.x < self.x-dx: b.vx += _ctx.random(dx)<EOL>if b.y < self.y-dy: b.vy += _ctx.random(dy)<EOL>if b.x > self.x+self.w+dx: b.vx -= _ctx.random(dx)<EOL>if b.y > self.y+self.h+dy: b.vy -= _ctx.random(dy)<EOL>if b.z < <NUM_LIT:0>: b.vz += <NUM_LIT:10><EOL>if b.z > <NUM_LIT:100>: b.vz -= <NUM_LIT:10><EOL>if b.y > self._perch_y and _ctx.random() < self._perch:<EOL><INDENT>b.y = self._perch_y<EOL>b.vy = -abs(b.vy) * <NUM_LIT><EOL>b.is_perching = True<EOL>try:<EOL><INDENT>b._perch_t = self._perch_t()<EOL><DEDENT>except:<EOL><INDENT>b._perch_t = self._perch_t<EOL><DEDENT><DEDENT><DEDENT>

Cages the flock inside the x, y, w, h area.

        The actual cage is a bit larger,
        so boids don't seem to bounce of invisible walls
        (they are rather "encouraged" to stay in the area).

        If a boid touches the ground level,
        it may decide to perch there for a while.


def update(self, <EOL>shuffled=True, <EOL>cohesion=<NUM_LIT:100>, <EOL>separation=<NUM_LIT:10>, <EOL>alignment=<NUM_LIT:5>, <EOL>goal=<NUM_LIT:20>,<EOL>limit=<NUM_LIT:30>):

<EOL>from random import shuffle<EOL>if shuffled: shuffle(self)<EOL>m1 = <NUM_LIT:1.0> <EOL>m2 = <NUM_LIT:1.0> <EOL>m3 = <NUM_LIT:1.0> <EOL>m4 = <NUM_LIT:1.0> <EOL>if not self.scattered and _ctx.random() < self._scatter:<EOL><INDENT>self.scattered = True<EOL><DEDENT>if self.scattered:<EOL><INDENT>m1 = -m1<EOL>m3 *= <NUM_LIT><EOL>self._scatter_i += <NUM_LIT:1><EOL><DEDENT>if self._scatter_i >= self._scatter_t:<EOL><INDENT>self.scattered = False<EOL>self._scatter_i = <NUM_LIT:0><EOL><DEDENT>if not self.has_goal:<EOL><INDENT>m4 = <NUM_LIT:0><EOL><DEDENT>if self.flee:<EOL><INDENT>m4 = -m4<EOL><DEDENT>for b in self:<EOL><INDENT>if b.is_perching:<EOL><INDENT>if b._perch_t > <NUM_LIT:0>:<EOL><INDENT>b._perch_t -= <NUM_LIT:1><EOL>continue<EOL><DEDENT>else:<EOL><INDENT>b.is_perching = False<EOL><DEDENT><DEDENT>vx1, vy1, vz1 = b.cohesion(cohesion)<EOL>vx2, vy2, vz2 = b.separation(separation)<EOL>vx3, vy3, vz3 = b.alignment(alignment)<EOL>vx4, vy4, vz4 = b.goal(self._gx, self._gy, self._gz, goal)<EOL>b.vx += m1*vx1 + m2*vx2 + m3*vx3 + m4*vx4<EOL>b.vy += m1*vy1 + m2*vy2 + m3*vy3 + m4*vy4<EOL>b.vz += m1*vz1 + m2*vz2 + m3*vz3 + m4*vz4<EOL>b.limit(limit)<EOL>b.x += b.vx<EOL>b.y += b.vy<EOL>b.z += b.vz<EOL><DEDENT>self.constrain()<EOL>

Calculates the next motion frame for the flock.


return self.find("<STR_LIT:title>").string<EOL>


meta = self.find("<STR_LIT>", {"<STR_LIT:name>":"<STR_LIT:description>"})<EOL>if isinstance(meta, dict) andmeta.has_key("<STR_LIT:content>"):<EOL><INDENT>return meta["<STR_LIT:content>"]<EOL><DEDENT>else:<EOL><INDENT>return u"<STR_LIT>"<EOL><DEDENT>

Returns the meta description in the page.


meta = self.find("<STR_LIT>", {"<STR_LIT:name>":"<STR_LIT>"})<EOL>if isinstance(meta, dict) andmeta.has_key("<STR_LIT:content>"):<EOL><INDENT>keywords = [k.strip() for k in meta["<STR_LIT:content>"].split("<STR_LIT:U+002C>")]<EOL><DEDENT>else:<EOL><INDENT>keywords = []<EOL><DEDENT>return keywords<EOL>


domain = URLParser(self.url).domain<EOL>links = []<EOL>for a in self("<STR_LIT:a>"):<EOL><INDENT>for attribute, value in a.attrs:<EOL><INDENT>if attribute == "<STR_LIT>":<EOL><INDENT>if not externalor (value.startswith("<STR_LIT>") and value.find("<STR_LIT>"+domain) < <NUM_LIT:0>):<EOL><INDENT>links.append(value)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>return links<EOL>

Retrieves links in the page.

        Returns a list of URL's.
        By default, only external URL's are returned.
        External URL's starts with http:// and point to another
        domain than the domain the page is on.


def __init__(self, name, type="<STR_LIT>"):

self.path = os.path.join(CACHE_PATH, name)<EOL>self.type = type<EOL>if not os.path.exists(self.path):<EOL><INDENT>os.makedirs(self.path)<EOL><DEDENT>

The cache can be used to store data downloads.

        All of the data is stored in subfolders of the CACHE_PATH.
        Each filename is hashed to a unique md5 string.


h = md5(id).hexdigest()<EOL>return os.path.join(self.path, h+self.type)<EOL>

Creates a unique filename in the cache for the id.


path = self.hash(id)<EOL>if os.path.exists(path):<EOL><INDENT>modified = datetime.datetime.fromtimestamp(os.stat(path)[<NUM_LIT:8>])<EOL>age = datetime.datetime.today() - modified<EOL>return age.days<EOL><DEDENT>else:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>

Returns the age of the cache entry, in days.


return s.encode("<STR_LIT:utf-8>")<EOL>


def search(q, start=<NUM_LIT:0>, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = GOOGLE_SEARCH<EOL>return GoogleSearch(q, start, service, "<STR_LIT>", wait, asynchronous, cached)<EOL>

Returns a Google web query formatted as a GoogleSearch list object.


def search_images(q, start=<NUM_LIT:0>, size="<STR_LIT>", wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = GOOGLE_IMAGES<EOL>return GoogleSearch(q, start, service, size, wait, asynchronous, cached)<EOL>

Returns a Google images query formatted as a GoogleSearch list object.


def search_news(q, start=<NUM_LIT:0>, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = GOOGLE_NEWS<EOL>return GoogleSearch(q, start, service, "<STR_LIT>", wait, asynchronous, cached)<EOL>

Returns a Google news query formatted as a GoogleSearch list object.


def search_blogs(q, start=<NUM_LIT:0>, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = GOOGLE_BLOGS<EOL>return GoogleSearch(q, start, service, "<STR_LIT>", wait, asynchronous, cached)<EOL>

Returns a Google blogs query formatted as a GoogleSearch list object.


def sort(words, context="<STR_LIT>", strict=True, relative=True, service=GOOGLE_SEARCH,<EOL>wait=<NUM_LIT:10>, asynchronous=False, cached=False):

results = []<EOL>for word in words:<EOL><INDENT>q = word + "<STR_LIT:U+0020>" + context<EOL>q.strip()<EOL>if strict: q = "<STR_LIT>"+q+"<STR_LIT>"<EOL>r = GoogleSearch(q, <NUM_LIT:1>, service, "<STR_LIT>", wait, asynchronous, cached)<EOL>results.append(r)<EOL><DEDENT>results.sort(GoogleResults.__cmp__)<EOL>results.reverse()<EOL>if relative and len(results) > <NUM_LIT:0>:<EOL><INDENT>sum = <NUM_LIT><EOL>for r in results: sum += r.total<EOL>for r in results: r.total /= float(sum)<EOL><DEDENT>results = [(r.query, r.total) for r in results]<EOL>return results<EOL>

Performs a Google sort on the given list.

    Sorts the items in the list according to
    the result count Google yields on an item.

    Setting a context sorts the items according
    to their relation to this context;
    for example sorting [red, green, blue] by "love"
    yields red as the highest results,
    likely because red is the color commonly associated with love.


str = replace_entities(str)<EOL>str = strip_tags(str)<EOL>str = collapse_spaces(str)<EOL>return str<EOL>

Parses the text data from an XML element defined by tag.


if self.total > other.total:<EOL><INDENT>return <NUM_LIT:1><EOL><DEDENT>elif self.total < other.total: <EOL><INDENT>return -<NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>

Compares with another GoogleSearch based on the number of results.


def __init__(self, q, start=<NUM_LIT:0>, service=GOOGLE_SEARCH, size="<STR_LIT>",<EOL>wait=<NUM_LIT:10>, asynchronous=False, cached=True):

self.query = q<EOL>self.service = service<EOL>if cached:<EOL><INDENT>cache = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>cache = None<EOL><DEDENT>url = "<STR_LIT>"<EOL>url = "<STR_LIT>"<EOL>if service == GOOGLE_SEARCH : url += "<STR_LIT>"<EOL>if service == GOOGLE_IMAGES : <EOL><INDENT>url += "<STR_LIT>"<EOL><DEDENT>if service == GOOGLE_NEWS   : url += "<STR_LIT>"<EOL>if service == GOOGLE_BLOGS  : url += "<STR_LIT>"<EOL>arg = urllib.urlencode((("<STR_LIT:v>", <NUM_LIT:1.0>),<EOL>("<STR_LIT:q>", q),<EOL>("<STR_LIT:start>", start),<EOL>("<STR_LIT>", "<STR_LIT>"),<EOL>("<STR_LIT:key>", GOOGLE_ID),<EOL>("<STR_LIT>", disambiguate_size(size))))<EOL>url += arg<EOL>URLAccumulator.__init__(self, url, wait, asynchronous, cache, "<STR_LIT>")<EOL>

Searches Google for the given query.

        By default, return cached results whenever possible.
        Otherwise, go online and update the local cache.
        The number of results is limited to 8 and starts at the given index.
        You can only return up to 32 results.

        The returned results depend on the service used:
        web pages, images, news or blogs.


def search(q, start=<NUM_LIT:1>, count=<NUM_LIT:10>, context=None, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = YAHOO_SEARCH<EOL>return YahooSearch(q, start, count, service, context, wait, asynchronous, cached)<EOL>

Returns a Yahoo web query formatted as a YahooSearch list object.


def search_images(q, start=<NUM_LIT:1>, count=<NUM_LIT:10>, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = YAHOO_IMAGES<EOL>return YahooSearch(q, start, count, service, None, wait, asynchronous, cached)<EOL>

Returns a Yahoo images query formatted as a YahooSearch list object.


def search_news(q, start=<NUM_LIT:1>, count=<NUM_LIT:10>, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

service = YAHOO_NEWS<EOL>return YahooSearch(q, start, count, service, None, wait, asynchronous, cached)<EOL>

Returns a Yahoo news query formatted as a YahooSearch list object.


def suggest_spelling(q, wait=<NUM_LIT:10>, asynchronous=False, cached=False):

return YahooSpelling(q, wait, asynchronous, cached)<EOL>

Returns list of suggested spelling corrections for the given query.


def sort(words, context="<STR_LIT>", strict=True, relative=True, service=YAHOO_SEARCH,<EOL>wait=<NUM_LIT:10>, asynchronous=False, cached=False):

results = []<EOL>for word in words:<EOL><INDENT>q = word + "<STR_LIT:U+0020>" + context<EOL>q.strip()<EOL>if strict: q = "<STR_LIT>"+q+"<STR_LIT>"<EOL>r = YahooSearch(q, <NUM_LIT:1>, <NUM_LIT:1>, service, context, wait, asynchronous, cached)<EOL>results.append(r)<EOL><DEDENT>results.sort(YahooResults.__cmp__)<EOL>results.reverse()<EOL>if relative and len(results) > <NUM_LIT:0>:<EOL><INDENT>sum = <NUM_LIT><EOL>for r in results: sum += r.total<EOL>for r in results: r.total /= float(sum)<EOL><DEDENT>results = [(r.query, r.total) for r in results]<EOL>return results<EOL>

Performs a Yahoo sort on the given list.

    Sorts the items in the list according to
    the result count Yahoo yields on an item.

    Setting a context sorts the items according
    to their relation to this context;
    for example sorting [red, green, blue] by "love"
    yields red as the highest results,
    likely because red is the color commonly associated with love.


tags = e.getElementsByTagName(tag)<EOL>children = tags[<NUM_LIT:0>].childNodes<EOL>if len(children) != <NUM_LIT:1>: return None<EOL>assert children[<NUM_LIT:0>].nodeType == xml.dom.minidom.Element.TEXT_NODE<EOL>s = children[<NUM_LIT:0>].nodeValue<EOL>s = format_data(s)<EOL>s = replace_entities(s)<EOL>return s<EOL>


Compares with another YahooSearch based on the number of results.


def __init__(self, q, start=<NUM_LIT:1>, count=<NUM_LIT:10>, service=YAHOO_SEARCH, context=None, <EOL>wait=<NUM_LIT:10>, asynchronous=False, cached=True):

self.query = q<EOL>self.service = service<EOL>if cached:<EOL><INDENT>cache = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>cache = None<EOL><DEDENT>url = "<STR_LIT>"<EOL>if service == YAHOO_SEARCH and context == None : url += "<STR_LIT>"<EOL>if service == YAHOO_SEARCH and context != None : url += "<STR_LIT>"<EOL>if service == YAHOO_IMAGES   :  url += "<STR_LIT>"<EOL>if service == YAHOO_NEWS     :  url += "<STR_LIT>"<EOL>if service == YAHOO_SPELLING :  url += "<STR_LIT>"<EOL>arg = urllib.urlencode((("<STR_LIT>", YAHOO_ID), <EOL>("<STR_LIT>", q),<EOL>("<STR_LIT:start>", start),<EOL>("<STR_LIT>", count),<EOL>("<STR_LIT>", unicode(context))))<EOL>url += arg<EOL>URLAccumulator.__init__(self, url, wait, asynchronous, cache, "<STR_LIT>")<EOL>

Searches Yahoo for the given query.

        By default, return cached results whenever possible.
        Otherwise, go online and update the local cache.
        The number of results is limited to count and starts at the given index.

        The returned results depend on the service used:
        web pages, images, news, spelling suggestion or contextual links.


def download(self, size=SIZE_LARGE, thumbnail=False, wait=<NUM_LIT>, asynchronous=False):

if thumbnail == True: size = SIZE_THUMBNAIL <EOL>self._size = disambiguate_size(size)<EOL>if self._size == SIZE_THUMBNAIL:<EOL><INDENT>url = self.url.replace("<STR_LIT>", "<STR_LIT>")<EOL><DEDENT>else:<EOL><INDENT>url = self.url<EOL><DEDENT>cache = "<STR_LIT>"<EOL>extension = os.path.splitext(url)[<NUM_LIT:1>]<EOL>URLAccumulator.__init__(self, url, wait, asynchronous, cache, extension, <NUM_LIT:2>)<EOL>if not asynchronous:<EOL><INDENT>return self.path<EOL><DEDENT>

Downloads this image to cache.

        Calling the download() method instantiates an asynchronous URLAccumulator.
        Once it is done downloading, this image will have its path property
        set to an image file in the cache.


def replace(match):<EOL><INDENT>return ESCAPE_DCT[match.group(<NUM_LIT:0>)]<EOL><DEDENT>return '<STR_LIT:">' + ESCAPE.sub(replace, s) + '<STR_LIT:">'<EOL>

Return a JSON representation of a Python string


def __init__(self, skipkeys=False, ensure_ascii=True,<EOL>check_circular=True, allow_nan=True, sort_keys=False,<EOL>indent=None, separators=None, encoding='<STR_LIT:utf-8>', default=None):

self.skipkeys = skipkeys<EOL>self.ensure_ascii = ensure_ascii<EOL>self.check_circular = check_circular<EOL>self.allow_nan = allow_nan<EOL>self.sort_keys = sort_keys<EOL>self.indent = indent<EOL>self.current_indent_level = <NUM_LIT:0><EOL>if separators is not None:<EOL><INDENT>self.item_separator, self.key_separator = separators<EOL><DEDENT>if default is not None:<EOL><INDENT>self.default = default<EOL><DEDENT>self.encoding = encoding<EOL>

Constructor for JSONEncoder, with sensible defaults.

If skipkeys is False, then it is a TypeError to attempt
encoding of keys that are not str, int, long, float or None.  If
skipkeys is True, such items are simply skipped.

If ensure_ascii is True, the output is guaranteed to be str
objects with all incoming unicode characters escaped.  If
ensure_ascii is false, the output will be unicode object.

If check_circular is True, then lists, dicts, and custom encoded
objects will be checked for circular references during encoding to
prevent an infinite recursion (which would cause an OverflowError).
Otherwise, no such check takes place.

If allow_nan is True, then NaN, Infinity, and -Infinity will be
encoded as such.  This behavior is not JSON specification compliant,
but is consistent with most JavaScript based encoders and decoders.
Otherwise, it will be a ValueError to encode such floats.

If sort_keys is True, then the output of dictionaries will be
sorted by key; this is useful for regression tests to ensure
that JSON serializations can be compared on a day-to-day basis.

If indent is a non-negative integer, then JSON array
elements and object members will be pretty-printed with that
indent level.  An indent level of 0 will only insert newlines.
None is the most compact representation.

If specified, separators should be a (item_separator, key_separator)
tuple.  The default is (', ', ': ').  To get the most compact JSON
representation you should specify (',', ':') to eliminate whitespace.

If specified, default is a function that gets called for objects
that can't otherwise be serialized.  It should return a JSON encodable
version of the object or raise a ``TypeError``.

If encoding is not None, then all input strings will be
transformed into unicode using that encoding prior to JSON-encoding.
The default is UTF-8.


raise TypeError("<STR_LIT>" % (o,))<EOL>

Implement this method in a subclass such that it returns
a serializable object for ``o``, or calls the base implementation
(to raise a ``TypeError``).

For example, to support arbitrary iterators, you could
implement default like this::

    def default(self, o):
        try:
            iterable = iter(o)
        except TypeError:
            pass
        else:
            return list(iterable)
        return JSONEncoder.default(self, o)


<EOL>if isinstance(o, basestring):<EOL><INDENT>if isinstance(o, str):<EOL><INDENT>_encoding = self.encoding<EOL>if (_encoding is not None <EOL>and not (_encoding == '<STR_LIT:utf-8>')):<EOL><INDENT>o = o.decode(_encoding)<EOL><DEDENT><DEDENT>if self.ensure_ascii:<EOL><INDENT>return encode_basestring_ascii(o)<EOL><DEDENT>else:<EOL><INDENT>return encode_basestring(o)<EOL><DEDENT><DEDENT>chunks = list(self.iterencode(o))<EOL>return '<STR_LIT>'.join(chunks)<EOL>

Return a JSON string representation of a Python data structure.

>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'


if self.check_circular:<EOL><INDENT>markers = {}<EOL><DEDENT>else:<EOL><INDENT>markers = None<EOL><DEDENT>return self._iterencode(o, markers)<EOL>

Encode the given object and yield each string
representation as available.

For example::

    for chunk in JSONEncoder().iterencode(bigobject):
        mysocket.write(chunk)


def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,<EOL>allow_nan=True, cls=None, indent=None, separators=None,<EOL>encoding='<STR_LIT:utf-8>', default=None, **kw):

<EOL>if (skipkeys is False and ensure_ascii is True and<EOL>check_circular is True and allow_nan is True and<EOL>cls is None and indent is None and separators is None and<EOL>encoding == '<STR_LIT:utf-8>' and default is None and not kw):<EOL><INDENT>iterable = _default_encoder.iterencode(obj)<EOL><DEDENT>else:<EOL><INDENT>if cls is None:<EOL><INDENT>cls = JSONEncoder<EOL><DEDENT>iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,<EOL>check_circular=check_circular, allow_nan=allow_nan, indent=indent,<EOL>separators=separators, encoding=encoding,<EOL>default=default, **kw).iterencode(obj)<EOL><DEDENT>for chunk in iterable:<EOL><INDENT>fp.write(chunk)<EOL><DEDENT>

Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object).

If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
will be skipped instead of raising a ``TypeError``.

If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
may be ``unicode`` instances, subject to normal Python ``str`` to
``unicode`` coercion rules. Unless ``fp.write()`` explicitly
understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
to cause an error.

If ``check_circular`` is ``False``, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).

If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
in strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).

If ``indent`` is a non-negative integer, then JSON array elements and object
members will be pretty-printed with that indent level. An indent level
of 0 will only insert newlines. ``None`` is the most compact representation.

If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.

``encoding`` is the character encoding for str instances, default is UTF-8.

``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.

To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.


def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,<EOL>allow_nan=True, cls=None, indent=None, separators=None,<EOL>encoding='<STR_LIT:utf-8>', default=None, **kw):

<EOL>if (skipkeys is False and ensure_ascii is True and<EOL>check_circular is True and allow_nan is True and<EOL>cls is None and indent is None and separators is None and<EOL>encoding == '<STR_LIT:utf-8>' and default is None and not kw):<EOL><INDENT>return _default_encoder.encode(obj)<EOL><DEDENT>if cls is None:<EOL><INDENT>cls = JSONEncoder<EOL><DEDENT>return cls(<EOL>skipkeys=skipkeys, ensure_ascii=ensure_ascii,<EOL>check_circular=check_circular, allow_nan=allow_nan, indent=indent,<EOL>separators=separators, encoding=encoding, default=default,<EOL>**kw).encode(obj)<EOL>

Serialize ``obj`` to a JSON formatted ``str``.

If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
(``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
will be skipped instead of raising a ``TypeError``.

If ``ensure_ascii`` is ``False``, then the return value will be a
``unicode`` instance subject to normal Python ``str`` to ``unicode``
coercion rules instead of being escaped to an ASCII ``str``.

If ``check_circular`` is ``False``, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).

If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
strict compliance of the JSON specification, instead of using the
JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).

If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
representation.

If ``separators`` is an ``(item_separator, dict_separator)`` tuple
then it will be used instead of the default ``(', ', ': ')`` separators.
``(',', ':')`` is the most compact JSON representation.

``encoding`` is the character encoding for str instances, default is UTF-8.

``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.

To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
the ``cls`` kwarg.


def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,<EOL>parse_int=None, parse_constant=None, **kw):

return loads(fp.read(),<EOL>encoding=encoding, cls=cls, object_hook=object_hook,<EOL>parse_float=parse_float, parse_int=parse_int,<EOL>parse_constant=parse_constant, **kw)<EOL>

Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
a JSON document) to a Python object.

If the contents of ``fp`` is encoded with an ASCII based encoding other
than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
be specified. Encodings that are not ASCII based (such as UCS-2) are
not allowed, and should be wrapped with
``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
object and passed to ``loads()``

``object_hook`` is an optional function that will be called with the
result of any object literal decode (a ``dict``). The return value of
``object_hook`` will be used instead of the ``dict``. This feature
can be used to implement custom decoders (e.g. JSON-RPC class hinting).

To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg.


def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,<EOL>parse_int=None, parse_constant=None, **kw):

if (cls is None and encoding is None and object_hook is None and<EOL>parse_int is None and parse_float is None and<EOL>parse_constant is None and not kw):<EOL><INDENT>return _default_decoder.decode(s)<EOL><DEDENT>if cls is None:<EOL><INDENT>cls = JSONDecoder<EOL><DEDENT>if object_hook is not None:<EOL><INDENT>kw['<STR_LIT>'] = object_hook<EOL><DEDENT>if parse_float is not None:<EOL><INDENT>kw['<STR_LIT>'] = parse_float<EOL><DEDENT>if parse_int is not None:<EOL><INDENT>kw['<STR_LIT>'] = parse_int<EOL><DEDENT>if parse_constant is not None:<EOL><INDENT>kw['<STR_LIT>'] = parse_constant<EOL><DEDENT>return cls(encoding=encoding, **kw).decode(s)<EOL>

Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
document) to a Python object.

If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
must be specified. Encodings that are not ASCII based (such as UCS-2)
are not allowed and should be decoded to ``unicode`` first.

``object_hook`` is an optional function that will be called with the
result of any object literal decode (a ``dict``). The return value of
``object_hook`` will be used instead of the ``dict``. This feature
can be used to implement custom decoders (e.g. JSON-RPC class hinting).

``parse_float``, if specified, will be called with the string
of every JSON float to be decoded. By default this is equivalent to
float(num_str). This can be used to use another datatype or parser
for JSON floats (e.g. decimal.Decimal).

``parse_int``, if specified, will be called with the string
of every JSON int to be decoded. By default this is equivalent to
int(num_str). This can be used to use another datatype or parser
for JSON integers (e.g. float).

``parse_constant``, if specified, will be called with one of the
following strings: -Infinity, Infinity, NaN, null, true, false.
This can be used to raise an exception if invalid JSON numbers
are encountered.

To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg.


import warnings<EOL>warnings.warn("<STR_LIT>",<EOL>DeprecationWarning)<EOL>return loads(s)<EOL>

demjson, python-cjson API compatibility hook. Use loads(s) instead.


import warnings<EOL>warnings.warn("<STR_LIT>",<EOL>DeprecationWarning)<EOL>return dumps(obj)<EOL>

demjson, python-cjson compatibility hook. Use dumps(s) instead.


jsonlib, JsonUtils, python-json, json-py API compatibility hook.
Use loads(s) instead.


jsonlib, JsonUtils, python-json, json-py API compatibility hook.
Use dumps(s) instead.


def __init__(self, encoding=None, object_hook=None, parse_float=None,<EOL>parse_int=None, parse_constant=None, strict=True):

self.encoding = encoding<EOL>self.object_hook = object_hook<EOL>self.parse_float = parse_float<EOL>self.parse_int = parse_int<EOL>self.parse_constant = parse_constant<EOL>self.strict = strict<EOL>

``encoding`` determines the encoding used to interpret any ``str``
objects decoded by this instance (utf-8 by default).  It has no
effect when decoding ``unicode`` objects.

Note that currently only encodings that are a superset of ASCII work,
strings of other encodings should be passed in as ``unicode``.

``object_hook``, if specified, will be called with the result
of every JSON object decoded and its return value will be used in
place of the given ``dict``.  This can be used to provide custom
deserializations (e.g. to support JSON-RPC class hinting).

``parse_float``, if specified, will be called with the string
of every JSON float to be decoded. By default this is equivalent to
float(num_str). This can be used to use another datatype or parser
for JSON floats (e.g. decimal.Decimal).

``parse_int``, if specified, will be called with the string
of every JSON int to be decoded. By default this is equivalent to
int(num_str). This can be used to use another datatype or parser
for JSON integers (e.g. float).

``parse_constant``, if specified, will be called with one of the
following strings: -Infinity, Infinity, NaN, null, true, false.
This can be used to raise an exception if invalid JSON numbers
are encountered.


def decode(self, s, _w=WHITESPACE.match):

obj, end = self.raw_decode(s, idx=_w(s, <NUM_LIT:0>).end())<EOL>end = _w(s, end).end()<EOL>if end != len(s):<EOL><INDENT>raise ValueError(errmsg("<STR_LIT>", s, end, len(s)))<EOL><DEDENT>return obj<EOL>

Return the Python representation of ``s`` (a ``str`` or ``unicode``
instance containing a JSON document)


kw.setdefault('<STR_LIT>', self)<EOL>try:<EOL><INDENT>obj, end = self._scanner.iterscan(s, **kw).next()<EOL><DEDENT>except StopIteration:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>return obj, end<EOL>

Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
with a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended.

This can be used to decode a JSON document from a string that may
have extraneous data at the end.


def iterscan(self, string, idx=<NUM_LIT:0>, context=None):

match = self.scanner.scanner(string, idx).match<EOL>actions = self.actions<EOL>lastend = idx<EOL>end = len(string)<EOL>while True:<EOL><INDENT>m = match()<EOL>if m is None:<EOL><INDENT>break<EOL><DEDENT>matchbegin, matchend = m.span()<EOL>if lastend == matchend:<EOL><INDENT>break<EOL><DEDENT>action = actions[m.lastindex]<EOL>if action is not None:<EOL><INDENT>rval, next_pos = action(m, context)<EOL>if next_pos is not None and next_pos != matchend:<EOL><INDENT>matchend = next_pos<EOL>match = self.scanner.scanner(string, matchend).match<EOL><DEDENT>yield rval, matchend<EOL><DEDENT>lastend = matchend<EOL><DEDENT>


p1 = "<STR_LIT>"<EOL>p2 = "<STR_LIT>"<EOL>self.links = []<EOL>for p in (p1,p2):<EOL><INDENT>for link in re.findall(p, self.description):<EOL><INDENT>self.links.append(link)<EOL><DEDENT>self.description = re.sub(p, "<STR_LIT>", self.description)<EOL><DEDENT>self.description = self.description.strip()<EOL>

Strips links from the definition and gathers them in a links property.


for chunk in str.split("<STR_LIT:\n>"):<EOL><INDENT>if  not chunk.startswith("<STR_LIT:U+0020>"):<EOL><INDENT>return False<EOL><DEDENT><DEDENT>return True<EOL>

Determines if an item in a paragraph is preformatted.

    If all of the lines in the markup start with a " "
    this indicates preformatted text.
    Preformatted is usually used for programming code.


for chunk in str.split("<STR_LIT:\n>"):<EOL><INDENT>chunk = chunk.replace("<STR_LIT:\t>", "<STR_LIT>")<EOL>if  not chunk.lstrip().startswith("<STR_LIT:*>")and not re.search(r"<STR_LIT>", chunk.lstrip()):<EOL><INDENT>return False<EOL><DEDENT><DEDENT>return True<EOL>

Determines if an item in a paragraph is a list.

    If all of the lines in the markup start with a "*" or "1."
    this indicates a list as parsed by parse_paragraphs().
    It can be drawn with draw_list().


str = str.strip()<EOL>if str.startswith("<STR_LIT>") and str.endswith("<STR_LIT>"):<EOL><INDENT>return True<EOL><DEDENT>else:<EOL><INDENT>return False<EOL><DEDENT>

Determines if an item in a paragraph is a LaTeX math equation.

    Math equations are wrapped in <math></math> tags.
    They can be drawn as an image using draw_math().


def draw_math(str, x, y, alpha=<NUM_LIT:1.0>):

try: from web import _ctx<EOL>except: pass<EOL>str = re.sub("<STR_LIT>", "<STR_LIT>", str.strip())<EOL>img = mimetex.gif(str)<EOL>w, h = _ctx.imagesize(img)<EOL>_ctx.image(img, x, y, alpha=alpha)<EOL>return w, h<EOL>

Uses mimetex to generate a GIF-image from the LaTeX equation.


try: from web import _ctx<EOL>except: pass<EOL>l = _ctx.lineheight()<EOL>_ctx.lineheight(<NUM_LIT:1>)<EOL>w = _ctx.textwidth(str)<EOL>_ctx.lineheight(l)<EOL>return w<EOL>

textwidth() reports incorrectly when lineheight() is smaller than 1.0


def draw_list(markup, x, y, w, padding=<NUM_LIT:5>, callback=None):

try: from web import _ctx<EOL>except: pass<EOL>i = <NUM_LIT:1><EOL>for chunk in markup.split("<STR_LIT:\n>"):<EOL><INDENT>if callback != None: <EOL><INDENT>callback(chunk, i)<EOL><DEDENT>m = re.search("<STR_LIT>", chunk.lstrip())<EOL>if m:<EOL><INDENT>indent = re.search("<STR_LIT>", chunk).start()*padding*<NUM_LIT:2><EOL>bullet = m.group(<NUM_LIT:1>)<EOL>dx = textwidth("<STR_LIT>")<EOL>chunk = chunk.lstrip(m.group(<NUM_LIT:1>)+"<STR_LIT:\t>")<EOL><DEDENT>if chunk.lstrip().startswith("<STR_LIT:*>"):<EOL><INDENT>indent = chunk.find("<STR_LIT:*>")*padding*<NUM_LIT:2><EOL>bullet = u"<STR_LIT>"<EOL>dx = textwidth("<STR_LIT:*>")<EOL>chunk = chunk.lstrip("<STR_LIT>")<EOL><DEDENT>_ctx.text(bullet, x+indent, y)<EOL>dx += padding + indent<EOL>_ctx.text(chunk, x+dx, y, width=w-dx)<EOL>y += _ctx.textheight(chunk, width=w-dx)<EOL>y += _ctx.textheight("<STR_LIT:U+0020>") * <NUM_LIT><EOL>i += <NUM_LIT:1><EOL><DEDENT>

Draws list markup with indentation in NodeBox.

    Draw list markup at x, y coordinates
    using indented bullets or numbers.
    The callback is a command that takes a str and an int.


def draw_table(table, x, y, w, padding=<NUM_LIT:5>):

try: from web import _ctx<EOL>except: pass<EOL>f = _ctx.fill()<EOL>_ctx.stroke(f)<EOL>h = _ctx.textheight("<STR_LIT:U+0020>") + padding*<NUM_LIT:2><EOL>row_y = y<EOL>if table.title != "<STR_LIT>":<EOL><INDENT>_ctx.fill(f)<EOL>_ctx.rect(x, row_y, w, h)<EOL>_ctx.fill(<NUM_LIT:1>)<EOL>_ctx.text(table.title, x+padding, row_y+_ctx.fontsize()+ padding)<EOL>row_y += h<EOL><DEDENT>rowspans = [<NUM_LIT:1> for i in range(<NUM_LIT:10>)]<EOL>previous_cell_w = <NUM_LIT:0><EOL>for row in table:<EOL><INDENT>cell_x = x<EOL>cell_w  = <NUM_LIT:1.0> * w<EOL>cell_w -= previous_cell_w * len([n for n in rowspans if n > <NUM_LIT:1>])<EOL>cell_w /= len(row)<EOL>cell_h = <NUM_LIT:0><EOL>for cell in row:<EOL><INDENT>this_h = _ctx.textheight(cell, width=cell_w-padding*<NUM_LIT:2>) + padding*<NUM_LIT:2><EOL>cell_h = max(cell_h, this_h)<EOL><DEDENT>i = <NUM_LIT:0><EOL>for cell in row:<EOL><INDENT>if rowspans[i] > <NUM_LIT:1>:<EOL><INDENT>rowspans[i] -= <NUM_LIT:1><EOL>cell_x += previous_cell_w<EOL>i += <NUM_LIT:1><EOL><DEDENT>m = re.search("<STR_LIT>", cell.properties)<EOL>if m:<EOL><INDENT>rowspan = int(m.group(<NUM_LIT:1>))<EOL>rowspans[i] = rowspan<EOL><DEDENT>else:<EOL><INDENT>rowspan = <NUM_LIT:1><EOL><DEDENT>_ctx.fill(f)<EOL>_ctx.text(cell, cell_x+padding, row_y+_ctx.fontsize()+padding, cell_w-padding*<NUM_LIT:2>)<EOL>_ctx.line(cell_x, row_y, cell_x+cell_w, row_y)<EOL>if cell_x > x:<EOL><INDENT>_ctx.nofill()<EOL>_ctx.line(cell_x, row_y, cell_x, row_y+cell_h)<EOL><DEDENT>cell_x += cell_w<EOL>i += <NUM_LIT:1><EOL><DEDENT>row_y += cell_h<EOL>previous_cell_w = cell_w<EOL><DEDENT>_ctx.nofill()<EOL>_ctx.rect(x, y, w, row_y-y)<EOL>

This is a very poor algorithm to draw Wikipedia tables in NodeBox.


def __init__(self, title, markup, light=False, full_strip=True):

self.title = title<EOL>self.markup = markup<EOL>self.full_strip = full_strip<EOL>self.disambiguation = []<EOL>self.categories = []<EOL>self.links = []<EOL>self.paragraphs = []<EOL>self.images = []<EOL>self.tables = []<EOL>self.references = []<EOL>self.translations = {}<EOL>self.important = []<EOL>self.re = {<EOL>"<STR_LIT>" : r"<STR_LIT>",<EOL>"<STR_LIT>"       : r"<STR_LIT>",<EOL>"<STR_LIT>"           : r"<STR_LIT>",<EOL>"<STR_LIT:image>"          : re.compile(r"<STR_LIT>", re.I),<EOL>"<STR_LIT>"        : re.compile("<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT>"          : re.compile(r"<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT>"     : re.compile(r"<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT>"      : re.compile(r"<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT>"       : re.compile(r"<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT:url>"            : r"<STR_LIT>",            <EOL>"<STR_LIT>"   : re.compile(r"<STR_LIT>", re.DOTALL),<EOL>"<STR_LIT>"    : r"<STR_LIT>",<EOL>"<STR_LIT>"           : r"<STR_LIT>",<EOL>"<STR_LIT>"        : re.compile(r"<STR_LIT>", re.DOTALL),       <EOL>}<EOL>self.ref = "<STR_LIT>"<EOL>self.parse(light)<EOL>

Wikipedia page parser.

        The expected markup is the stuff in Wikipedia's edit textarea.
        With light=True, it will onlt parse links to other articles (which is faster).
        With full_strip=False, it will preserve some HTML markup (links, bold, italic).


markup = self.markup<EOL>self.disambiguation = self.parse_disambiguation(markup)<EOL>self.categories = self.parse_categories(markup)<EOL>self.links = self.parse_links(markup)<EOL>if not light:<EOL><INDENT>markup = self.convert_pre(markup)<EOL>markup = self.convert_li(markup)<EOL>markup = self.convert_table(markup)<EOL>markup = replace_entities(markup)<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>self.references, markup = self.parse_references(markup)<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>self.images, markup = self.parse_images(markup)<EOL>self.images.extend(self.parse_gallery_images(markup))<EOL>self.paragraphs = self.parse_paragraphs(markup)<EOL>self.tables = self.parse_tables(markup)<EOL>self.translations = self.parse_translations(markup)<EOL>self.important = self.parse_important(markup)<EOL><DEDENT>

Parses data from Wikipedia page markup.

        The markup comes from Wikipedia's edit page.
        We parse it here into objects containing plain text.
        The light version parses only links to other articles, it's faster than a full parse.


<EOL>if self.full_strip:<EOL><INDENT>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL><DEDENT>else:<EOL><INDENT>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL><DEDENT>markup = re.sub(self.re["<STR_LIT>"], "<STR_LIT>", markup)<EOL>markup = re.sub(self.re["<STR_LIT>"], "<STR_LIT>", markup)<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>if self.full_strip:<EOL><INDENT>markup = re.sub(r"<STR_LIT>", "<STR_LIT>", markup)<EOL><DEDENT>else:<EOL><INDENT>markup = re.sub(r"<STR_LIT>", '<STR_LIT>', markup)<EOL>markup = re.sub(r"<STR_LIT>", '<STR_LIT>', markup)    <EOL><DEDENT>markup = re.sub(self.re["<STR_LIT>"], "<STR_LIT>", markup)<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>markup = markup.replace("<STR_LIT:[>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT:]>", "<STR_LIT>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT:[>")<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT:]>")<EOL>markup = re.sub(self.re["<STR_LIT>"], "<STR_LIT>", markup)                  <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)                      <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)           <EOL>markup = re.sub(self.ref+"<STR_LIT>", "<STR_LIT>", markup)        <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)                 <EOL>markup = re.sub(self.re["<STR_LIT>"], "<STR_LIT>", markup)                    <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)            <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)                   <EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)         <EOL>markup = re.sub(re.compile("<STR_LIT>", re.DOTALL), "<STR_LIT>", markup)  <EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT:U+0020>", markup)<EOL>markup = markup.split("<STR_LIT:\n>")<EOL>for i in range(len(markup)):<EOL><INDENT>if not markup[i].startswith("<STR_LIT:U+0020>"):<EOL><INDENT>markup[i] = re.sub(r"<STR_LIT>", "<STR_LIT:U+0020>", markup[i])<EOL><DEDENT><DEDENT>markup = "<STR_LIT:\n>".join(markup)<EOL>markup = markup.replace("<STR_LIT>", "<STR_LIT:.>")<EOL>if self.full_strip:<EOL><INDENT>markup = strip_tags(markup, exclude=["<STR_LIT>"], linebreaks=True)<EOL><DEDENT>markup = markup.strip()<EOL>return markup<EOL>

Strips Wikipedia markup from given text.

        This creates a "plain" version of the markup,
        stripping images and references and the like.
        Does some commonsense maintenance as well,
        like collapsing multiple spaces.
        If you specified full_strip=False for WikipediaPage instance,
        some markup is preserved as HTML (links, bold, italic).


for m in re.findall(self.re["<STR_LIT>"], markup):<EOL><INDENT>markup = markup.replace(m, m.replace("<STR_LIT:\n>", "<STR_LIT>"))<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL>markup = re.sub("<STR_LIT>", "<STR_LIT>", markup)<EOL><DEDENT>return markup<EOL>

Substitutes <pre> to Wikipedia markup by adding a space at the start of a line.


for li in re.findall("<STR_LIT>", markup):<EOL><INDENT>markup = re.sub(li, "<STR_LIT>", markup)<EOL><DEDENT>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>return markup<EOL>

Subtitutes <li> content to Wikipedia markup.


for table in re.findall(self.re["<STR_LIT>"], markup):<EOL><INDENT>wiki = table<EOL>wiki = re.sub(r"<STR_LIT>", "<STR_LIT>", wiki)<EOL>wiki = re.sub(r"<STR_LIT>", "<STR_LIT>", wiki)<EOL>wiki = re.sub(r"<STR_LIT>", "<STR_LIT>", wiki)<EOL>wiki = wiki.replace("<STR_LIT>", "<STR_LIT:\n>")<EOL>wiki = wiki.replace("<STR_LIT>", "<STR_LIT:\n>")<EOL>wiki = wiki.replace("<STR_LIT>", "<STR_LIT>")<EOL>markup = markup.replace(table, wiki)<EOL><DEDENT>return markup<EOL>

Subtitutes <table> content to Wikipedia markup.


links = []<EOL>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for link in m:<EOL><INDENT>if link.find("<STR_LIT:{>") >= <NUM_LIT:0>:<EOL><INDENT>link = re.sub("<STR_LIT>", "<STR_LIT>", link)<EOL>link = link.replace("<STR_LIT:{>", "<STR_LIT>")<EOL>link = link.replace("<STR_LIT:}>", "<STR_LIT>")            <EOL><DEDENT>link = link.split("<STR_LIT:|>")<EOL>link[<NUM_LIT:0>] = link[<NUM_LIT:0>].split("<STR_LIT:#>")<EOL>page = link[<NUM_LIT:0>][<NUM_LIT:0>].strip()<EOL>if not page in links:<EOL><INDENT>links.append(page)<EOL><DEDENT><DEDENT>links.sort()<EOL>return links<EOL>

Returns a list of internal Wikipedia links in the markup.

        # A Wikipedia link looks like:
        # [[List of operating systems#Embedded | List of embedded operating systems]]
        # It does not contain a colon, this indicates images, users, languages, etc.

        The return value is a list containing the first part of the link,
        without the anchor.


def parse_images(self, markup, treshold=<NUM_LIT:6>):

images = []<EOL>m = re.findall(self.re["<STR_LIT:image>"], markup)<EOL>for p in m:<EOL><INDENT>p = self.parse_balanced_image(p)<EOL>img = p.split("<STR_LIT:|>")<EOL>path = img[<NUM_LIT:0>].replace("<STR_LIT>", "<STR_LIT>").strip()<EOL>description = u"<STR_LIT>"<EOL>links = {}<EOL>properties = []<EOL>if len(img) > <NUM_LIT:1>:<EOL><INDENT>img = "<STR_LIT:|>".join(img[<NUM_LIT:1>:])<EOL>links = self.parse_links(img)<EOL>properties = self.plain(img).split("<STR_LIT:|>")<EOL>description = u"<STR_LIT>"<EOL>if len(properties[-<NUM_LIT:1>]) > treshold:<EOL><INDENT>description = properties[-<NUM_LIT:1>]<EOL>properties = properties[:-<NUM_LIT:1>]<EOL><DEDENT><DEDENT>img = WikipediaImage(path, description, links, properties)<EOL>images.append(img)<EOL>markup = markup.replace(p, "<STR_LIT>")<EOL><DEDENT>return images, markup.strip()<EOL>

Returns a list of images found in the markup.

        An image has a pathname, a description in plain text
        and a list of properties Wikipedia uses to size and place images.

        # A Wikipedia image looks like:
        # [[Image:Columbia Supercomputer - NASA Advanced Supercomputing Facility.jpg|right|thumb|
        #   The [[NASA]] [[Columbia (supercomputer)|Columbia Supercomputer]].]]
        # Parts are separated by "|".
        # The first part is the image file, the last part can be a description.
        # In between are display properties, like "right" or "thumb".


opened = <NUM_LIT:0><EOL>closed = <NUM_LIT:0><EOL>for i in range(len(markup)):<EOL><INDENT>if markup[i] == "<STR_LIT:[>": opened += <NUM_LIT:1><EOL>if markup[i] == "<STR_LIT:]>": closed += <NUM_LIT:1><EOL>if opened == closed:<EOL><INDENT>return markup[:i+<NUM_LIT:1>]<EOL><DEDENT><DEDENT>return markup<EOL>

Corrects Wikipedia image markup.

        Images have a description inside their link markup that
        can contain link markup itself, make sure the outer "[" and "]" brackets
        delimiting the image are balanced correctly (e.g. no [[ ]] ]]).

        Called from parse_images().


gallery = re.search(self.re["<STR_LIT>"], markup)<EOL>if gallery:<EOL><INDENT>gallery = gallery.group(<NUM_LIT:1>)<EOL>gallery = gallery.replace("<STR_LIT>", "<STR_LIT>")<EOL>gallery = gallery.replace("<STR_LIT:\n>", "<STR_LIT>")<EOL>images, markup = self.parse_images(gallery)<EOL>return images<EOL><DEDENT>return []<EOL>

Parses images from the <gallery></gallery> section.

        Images inside <gallery> tags do not have outer "[[" brackets.
        Add these and then parse again.


s = self.plain(markup)<EOL>s = re.sub(re.compile("<STR_LIT>", re.DOTALL), "<STR_LIT>", s)<EOL>s = re.sub("<STR_LIT>", "<STR_LIT>", s)<EOL>chunks = []<EOL>ch = "<STR_LIT>"<EOL>i = <NUM_LIT:1><EOL>for chunk in s.split("<STR_LIT:\n>"):<EOL><INDENT>if chunk.startswith("<STR_LIT::>"):<EOL><INDENT>chunk = chunk.lstrip("<STR_LIT::>")<EOL><DEDENT>if len(chunk.strip()) > <NUM_LIT:1>:<EOL><INDENT>if not chunk.startswith("<STR_LIT:|>"):<EOL><INDENT>ch += chunk + "<STR_LIT:\n>"<EOL><DEDENT><DEDENT>if ch.strip() != "<STR_LIT>":<EOL><INDENT>if not re.search("<STR_LIT>", chunk):<EOL><INDENT>ch = self.parse_paragraph_list(ch)<EOL>chunks.append(ch.rstrip())<EOL>ch = "<STR_LIT>"<EOL><DEDENT><DEDENT><DEDENT>if ch.strip() != "<STR_LIT>":<EOL><INDENT>ch = self.parse_paragraph_list(ch)<EOL>chunks.append(ch.strip())<EOL><DEDENT>return chunks<EOL>

Creates a list from lines of text in a paragraph.

        Each line of text is a new item in the list,
        except lists and preformatted chunks (<li> and <pre>),
        these are kept together as a single chunk.

        Lists are formatted using parse_paragraph_list().

        Empty lines are stripped from the output.
        Indentation (i.e. lines starting with ":") is ignored.

        Called from parse_paragraphs() method.


def parse_paragraph_list(self, markup, indent="<STR_LIT:\t>"):

def lastleft(ch, str):<EOL><INDENT>n = <NUM_LIT:0><EOL>while n < len(str) and str[n] == ch: n += <NUM_LIT:1><EOL>return n        <EOL><DEDENT>tally = [<NUM_LIT:1> for i in range(<NUM_LIT:10>)]<EOL>chunks = markup.split("<STR_LIT:\n>")<EOL>for i in range(len(chunks)):<EOL><INDENT>if chunks[i].startswith("<STR_LIT:#>"):<EOL><INDENT>j = min(lastleft("<STR_LIT:#>", chunks[i]), len(tally)-<NUM_LIT:1>)<EOL>chunks[i] = indent*(j-<NUM_LIT:1>) + str(tally[j])+"<STR_LIT>" + chunks[i][j:]<EOL>chunks[i] = chunks[i].replace("<STR_LIT>", "<STR_LIT>")<EOL>tally[j] += <NUM_LIT:1><EOL>for k in range(j+<NUM_LIT:1>, len(tally)): <EOL><INDENT>tally[k] = <NUM_LIT:1><EOL><DEDENT><DEDENT>if chunks[i].startswith("<STR_LIT:;>"):<EOL><INDENT>chunks[i] = "<STR_LIT:*>" + chunks[i][<NUM_LIT:1>:]<EOL><DEDENT>if chunks[i].startswith("<STR_LIT:*>"):<EOL><INDENT>j = lastleft("<STR_LIT:*>", chunks[i])  <EOL>chunks[i] = indent*(j-<NUM_LIT:1>) + "<STR_LIT>" + chunks[i][j:]<EOL>chunks[i] = chunks[i].replace("<STR_LIT>", "<STR_LIT>")<EOL><DEDENT><DEDENT>return "<STR_LIT:\n>".join(chunks)<EOL>

Formats bullets and numbering of Wikipedia lists.

        List items are marked by "*", "#" or ";" at the start of a line.
        We treat ";" the same as "*",
        and replace "#" with real numbering (e.g. "2.").
        Sublists (e.g. *** and ###) get indented by tabs.

        Called from parse_paragraphs() method.


return markup.count("<STR_LIT:=>")/<NUM_LIT:2> - <NUM_LIT:1><EOL>

Returns the depth of a heading.

        The depth determines parent and child relations,
        which headings (and hence which paragraphs) are a child to a heading higher up.
        Returns 0 for <h1> =, 1 for <h2> ==, etc.

        Called from parse_paragraphs() method.


def connect_paragraph(self, paragraph, paragraphs):

if paragraph.depth > <NUM_LIT:0>:<EOL><INDENT>n = range(len(paragraphs))<EOL>n.reverse()<EOL>for i in n:<EOL><INDENT>if paragraphs[i].depth == paragraph.depth-<NUM_LIT:1>:<EOL><INDENT>paragraph.parent = paragraphs[i]<EOL>paragraphs[i].children.append(paragraph)<EOL>break<EOL><DEDENT><DEDENT><DEDENT>return paragraph<EOL>

Create parent/child links to other paragraphs.

        The paragraphs parameters is a list of all the paragraphs
        parsed up till now.

        The parent is the previous paragraph whose depth is less.
        The parent's children include this paragraph.

        Called from parse_paragraphs() method.


for chunk in markup.split("<STR_LIT:\n>"):<EOL><INDENT>m = re.search(self.ref+"<STR_LIT>", chunk)<EOL>if m:<EOL><INDENT>chunk = chunk.strip("<STR_LIT>")<EOL>chunk = chunk.replace(m.group(<NUM_LIT:0>), "<STR_LIT>")<EOL>chunk = self.plain(chunk)<EOL>i = int(m.group(<NUM_LIT:1>))<EOL>if chunk != "<STR_LIT>":<EOL><INDENT>self.references[i-<NUM_LIT:1>].note = chunk<EOL><DEDENT><DEDENT>elif chunk.strip().startswith("<STR_LIT:*>")and chunk.find("<STR_LIT>") < <NUM_LIT:0>:<EOL><INDENT>chunk = chunk.strip("<STR_LIT>")<EOL>chunk = self.plain(chunk)<EOL>if chunk != "<STR_LIT>":<EOL><INDENT>r = WikipediaReference()<EOL>r.note = chunk<EOL>self.references.append(r)<EOL><DEDENT><DEDENT><DEDENT>

Updates references with content from specific paragraphs.

        The "references", "notes", "external links" paragraphs
        are double-checked for references. Not all items in the list
        might have been referenced inside the article, or the item
        might contain more info than we initially parsed from it.

        Called from parse_paragraphs() method.


<EOL>refs = ["<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>"]<EOL>exclude = ["<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>", "<STR_LIT>"]<EOL>exclude.extend(refs)<EOL>paragraphs = []<EOL>paragraph = WikipediaParagraph(self.title)<EOL>paragraph_data = "<STR_LIT>"<EOL>for chunk in markup.split("<STR_LIT:\n>"):<EOL><INDENT>if not chunk.startswith("<STR_LIT:U+0020>"):<EOL><INDENT>chunk = chunk.strip()<EOL><DEDENT>if chunk.startswith("<STR_LIT:=>"):<EOL><INDENT>if paragraph.title.lower() in refsor (paragraph.parent and paragraph.parent.title.lower() in refs):<EOL><INDENT>self.parse_paragraph_references(paragraph_data)<EOL><DEDENT>paragraph.extend(self.parse_paragraph(paragraph_data))<EOL>paragraphs.append(paragraph)<EOL>title = chunk.strip().strip("<STR_LIT:=>")<EOL>title = self.plain(title)<EOL>paragraph = WikipediaParagraph(title)<EOL>paragraph.depth = self.parse_paragraph_heading_depth(chunk)<EOL>if paragraph.title.lower() not in exclude:<EOL><INDENT>paragraph = self.connect_paragraph(paragraph, paragraphs)<EOL><DEDENT>paragraph_data = "<STR_LIT>"<EOL><DEDENT>elif re.search(re.compile("<STR_LIT>", re.I), chunk):<EOL><INDENT>paragraph.main = [link.strip("<STR_LIT>") for link in chunk.split("<STR_LIT:|>")[<NUM_LIT:1>:]]<EOL>paragraph.main = [re.sub(re.compile("<STR_LIT>", re.I), "<STR_LIT>", link) <EOL>for link in paragraph.main]<EOL><DEDENT>elif re.search(re.compile("<STR_LIT>", re.I), chunk):<EOL><INDENT>paragraph.related = [link.strip("<STR_LIT>") for link in chunk.split("<STR_LIT:|>")[<NUM_LIT:1>:]]<EOL><DEDENT>else:<EOL><INDENT>paragraph_data += chunk +"<STR_LIT:\n>"<EOL><DEDENT><DEDENT>if paragraph.title.lower() in refsor (paragraph.parent and paragraph.parent.title.lower() in refs):<EOL><INDENT>self.parse_paragraph_references(paragraph_data)<EOL><DEDENT>paragraph.extend(self.parse_paragraph(paragraph_data))<EOL>paragraphs.append(paragraph)<EOL>paragraphs_exclude = []<EOL>for paragraph in paragraphs:<EOL><INDENT>if paragraph.title.lower() not in excludeand not (paragraph.parent and paragraph.parent.title.lower() in exclude):<EOL><INDENT>paragraphs_exclude.append(paragraph)<EOL><DEDENT><DEDENT>if len(paragraphs_exclude) == <NUM_LIT:1> andlen(paragraphs_exclude[<NUM_LIT:0>]) == <NUM_LIT:0>:<EOL><INDENT>return []<EOL><DEDENT>return paragraphs_exclude<EOL>

Returns a list of paragraphs in the markup.

        A paragraph has a title and multiple lines of plain text.
        A paragraph might have parent and child paragraphs,
        denoting subtitles or bigger chapters.

        A paragraph might have links to additional articles.

        Formats numbered lists by replacing # by 1.
        Formats bulleted sublists like ** or *** with indentation.


if row == None:<EOL><INDENT>row = WikipediaTableRow()<EOL><DEDENT>markup = markup.replace("<STR_LIT>", "<STR_LIT>")<EOL>for cell in markup.lstrip("<STR_LIT>").split("<STR_LIT>"):<EOL><INDENT>i = cell.find("<STR_LIT:|>")<EOL>j = cell.find("<STR_LIT>")<EOL>if i><NUM_LIT:0> and (j<<NUM_LIT:0> or i<j):<EOL><INDENT>data = self.plain(cell[i+<NUM_LIT:1>:])<EOL>properties = cell[:i].strip()<EOL><DEDENT>else:<EOL><INDENT>data = self.plain(cell)<EOL>properties = u"<STR_LIT>"<EOL><DEDENT>cell = WikipediaTableCell(data)<EOL>cell.properties = properties<EOL>row.append(cell)<EOL><DEDENT>return row<EOL>

Parses a row of cells in a Wikipedia table.

        Cells in the row are separated by "||".
        A "!" indicates a row of heading columns.
        Each cell can contain properties before a "|",
        # e.g. align="right" | Cell 2 (right aligned).


def connect_table(self, table, chunk, markup):

k = markup.find(chunk)<EOL>i = markup.rfind("<STR_LIT>", <NUM_LIT:0>, k)<EOL>j = markup.find("<STR_LIT:\n>", i+<NUM_LIT:1>)<EOL>paragraph_title = markup[i:j].strip().strip("<STR_LIT>")<EOL>for paragraph in self.paragraphs:<EOL><INDENT>if paragraph.title == paragraph_title:<EOL><INDENT>paragraph.tables.append(table)<EOL>table.paragraph = paragraph<EOL><DEDENT><DEDENT>

Creates a link from the table to paragraph and vice versa.

        Finds the first heading above the table in the markup.
        This is the title of the paragraph the table belongs to.


tables = []<EOL>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for chunk in m:<EOL><INDENT>table = WikipediaTable()<EOL>table.properties = chunk.split("<STR_LIT:\n>")[<NUM_LIT:0>].strip("<STR_LIT>").strip()<EOL>self.connect_table(table, chunk, markup)<EOL>row = None<EOL>for chunk in chunk.split("<STR_LIT:\n>"):<EOL><INDENT>chunk = chunk.strip()<EOL>if chunk.startswith("<STR_LIT>"):<EOL><INDENT>title = self.plain(chunk.strip("<STR_LIT>"))<EOL>table.title = title<EOL><DEDENT>elif chunk.startswith("<STR_LIT>"):<EOL><INDENT>if row: <EOL><INDENT>row.properties = chunk.strip("<STR_LIT>").strip()<EOL>table.append(row)<EOL><DEDENT>row = None<EOL><DEDENT>elif chunk.startswith("<STR_LIT>"):<EOL><INDENT>pass<EOL><DEDENT>elif chunk.startswith("<STR_LIT:|>")or chunk.startswith("<STR_LIT:!>"):<EOL><INDENT>row = self.parse_table_row(chunk, row)<EOL><DEDENT><DEDENT>if row: table.append(row)<EOL>if len(table) > <NUM_LIT:0>:<EOL><INDENT>tables.append(table)<EOL><DEDENT><DEDENT>return tables<EOL>

Returns a list of tables in the markup.

        A Wikipedia table looks like:
        {| border="1"
        |-
        |Cell 1 (no modifier - not aligned)
        |-
        |align="right" |Cell 2 (right aligned)
        |-
        |}


references = []<EOL>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for reference in m:<EOL><INDENT>reference = re.sub("<STR_LIT>", "<STR_LIT>", reference)<EOL>if not reference.strip().startswith("<STR_LIT>") andnot re.search("<STR_LIT>", reference):<EOL><INDENT>r = WikipediaReference()<EOL>r.note = self.plain(re.sub("<STR_LIT>", "<STR_LIT>", reference))<EOL>if r.note != "<STR_LIT>":<EOL><INDENT>references.append(r)<EOL>p = "<STR_LIT:U+0020>"+self.ref+"<STR_LIT:(>"+str(len(references))+"<STR_LIT:)>"<EOL>markup = markup.replace(reference, p, <NUM_LIT:1>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT><DEDENT>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for citation in m:<EOL><INDENT>c = citation.replace("<STR_LIT:\n>", "<STR_LIT>")<EOL>r = WikipediaReference()<EOL>for key in r.__dict__.keys():<EOL><INDENT>value = re.search("<STR_LIT>"+key+"<STR_LIT>", c)<EOL>if value:<EOL><INDENT>value = value.group(<NUM_LIT:1>)<EOL>value = value.replace("<STR_LIT>", "<STR_LIT>")<EOL>value = value.strip().strip("<STR_LIT>")<EOL>value = self.plain(value)<EOL>setattr(r, key, value)<EOL><DEDENT><DEDENT>if r.first != "<STR_LIT>" and r.last != "<STR_LIT>":<EOL><INDENT>r.author = r.first + "<STR_LIT:U+0020>" + r.last<EOL><DEDENT>references.append(r)<EOL>p = "<STR_LIT:U+0020>"+self.ref+"<STR_LIT:(>"+str(len(references))+"<STR_LIT:)>"<EOL>markup = markup.replace(citation, p, <NUM_LIT:1>)<EOL><DEDENT>m = re.findall(self.re["<STR_LIT:url>"], markup)<EOL>for url in m:<EOL><INDENT>r = WikipediaReference()<EOL>i = url.find("<STR_LIT:U+0020>")<EOL>if i > <NUM_LIT:0>:<EOL><INDENT>r.url = url[:i].strip()<EOL>r.note = self.plain(url[i:])<EOL><DEDENT>else:<EOL><INDENT>r.url = url.strip()<EOL><DEDENT>references.append(r)<EOL>p = r.note+"<STR_LIT:U+0020>"+self.ref+"<STR_LIT:(>"+str(len(references))+"<STR_LIT:)>"<EOL>markup = markup.replace("<STR_LIT:[>"+url+"<STR_LIT:]>", p, <NUM_LIT:1>)<EOL><DEDENT>sorted = []<EOL>m = re.findall(self.ref+"<STR_LIT>", markup)<EOL>for i in m:<EOL><INDENT>sorted.append(references[int(i)-<NUM_LIT:1>])<EOL>markup = markup.replace(<EOL>self.ref+"<STR_LIT:(>"+i+"<STR_LIT:)>", <EOL>self.ref+"<STR_LIT>"+str(len(sorted))+"<STR_LIT:)>"<EOL>)<EOL><DEDENT>markup = markup.replace(self.ref+"<STR_LIT>", self.ref)<EOL>for r in references:<EOL><INDENT>if r not in sorted:<EOL><INDENT>sorted.append(r)<EOL><DEDENT><DEDENT>references = sorted<EOL>return references, markup.strip()<EOL>

Returns a list of references found in the markup.

        References appear inline as <ref> footnotes,
        http:// external links, or {{cite}} citations.
        We replace it with (1)-style footnotes.
        Additional references data is gathered in
        parse_paragraph_references() when we parse paragraphs.

        References can also appear in image descriptions,
        tables and taxoboxes, so they might not always pop up in a paragraph.

        The plain() method finally replaces (1) by [1].


categories = []<EOL>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for category in m:<EOL><INDENT>category = category.split("<STR_LIT:|>")<EOL>page = category[<NUM_LIT:0>].strip()<EOL>display = u"<STR_LIT>"<EOL>if len(category) > <NUM_LIT:1>: <EOL><INDENT>display = category[<NUM_LIT:1>].strip()<EOL><DEDENT>if not page in categories:<EOL><INDENT>categories.append(page)<EOL><DEDENT><DEDENT>return categories<EOL>

Returns a list of categories the page belongs to.

        # A Wikipedia category link looks like:
        # [[Category:Computing]]
        # This indicates the page is included in the given category.
        # If "Category" is preceded by ":" this indicates a link to a category.


global languages<EOL>translations = {}<EOL>m = re.findall(self.re["<STR_LIT>"], markup)<EOL>for language, translation in m:<EOL><INDENT>if language in languages:<EOL><INDENT>translations[language] = translation<EOL><DEDENT><DEDENT>return translations<EOL>

Returns a dictionary of translations for the page title.

        A Wikipedia language link looks like: [[af:Rekenaar]].
        The parser will also fetch links like "user:" and "media:"
        but these are stripped against the dictionary of
        Wikipedia languages.

        You can get a translated page by searching Wikipedia
        with the appropriate language code and supplying
        the translated title as query.