text:
<html>
<head>
<script src="../../lib/OpenLayers.js"></script>
  <script type="text/javascript">

    var name = "Vector Layer";

    function test_Layer_Vector_constructor(t) {
        t.plan(4);

        var options = {protocol: new OpenLayers.Protocol(),
                       strategies: [new OpenLayers.Strategy(), new OpenLayers.Strategy()]}
        var layer = new OpenLayers.Layer.Vector(name, options);

        t.ok(layer instanceof OpenLayers.Layer.Vector, "new OpenLayers.Layer.Vector returns correct object" );
        t.eq(layer.name, name, "layer name is correctly set");
        t.ok(layer.renderer.CLASS_NAME, "layer has a renderer");

        t.ok((layer.name == layer.strategies[0].layer.name) &&
             (layer.strategies[0].layer.name == layer.strategies[1].layer.name),
             "setLayer was called on strategies");
    }

    function test_Layer_Vector_refresh(t) {
        t.plan(4);

        var obj = {"an": "object"};

        var log;
        var layer = new OpenLayers.Layer.Vector(name, {
            eventListeners: {
                refresh: function(o) {
                    log.obj = o;
                }
            }
        });
        inRange = false;
        layer.calculateInRange = function() {
            return inRange;
        };

        log = {};
        inRange = false;
        layer.visibility = false;
        layer.refresh(obj);
        t.eq(log.obj, undefined, "[false, false] refresh not triggered");

        log = {};
        inRange = true;
        layer.visibility = false;
        layer.refresh(obj);
        t.eq(log.obj, undefined, "[true, false] refresh not triggered");

        log = {};
        inRange = false;
        layer.visibility = true;
        layer.refresh(obj);
        t.eq(log.obj, undefined, "[false, true] refresh not triggered");

        log = {};
        inRange = true;
        layer.visibility = true;
        layer.refresh(obj);
        t.ok(log.obj === obj, "[true, true] refresh triggered with correct arg");
    }

    function test_Layer_Vector_addFeatures(t) {
        t.plan(8);

        var layer = new OpenLayers.Layer.Vector(name);

        var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
        var pointFeature = new OpenLayers.Feature.Vector(point);

        layer.preFeatureInsert = function(feature) {
            t.ok(feature == pointFeature, "OpenLayers.Layer.Vector.addFeatures calls preFeatureInsert with the right arg");
        };
        layer.onFeatureInsert = function(feature) {
            t.ok(feature == pointFeature, "OpenLayers.Layer.Vector.addFeatures calls onFeatureInsert with the right arg");
        };
        layer.events.register('beforefeatureadded', null, function(obj) {
            t.ok(pointFeature == obj.feature, "OpenLayers.Layer.Vector.addFeatures triggers beforefeatureadded with correct feature passed to callback");
        });
        layer.events.register('featureadded', null, function(obj) {
            t.ok(pointFeature == obj.feature, "OpenLayers.Layer.Vector.addFeatures triggers featureadded with correct feature passed to callback");
        });
        layer.events.register('featuresadded', null, function(obj) {
            t.ok(pointFeature == obj.features[0], "OpenLayers.Layer.Vector.addFeatures triggers featuresadded with correct features passed to callback");
        });

        layer.addFeatures([pointFeature]);

        t.eq(layer.features.length, 1, "OpenLayers.Layer.Vector.addFeatures adds something to the array");
        t.ok(layer.features[0] == pointFeature, "OpenLayers.Layer.Vector.addFeatures returns an array of features");

        layer.preFeatureInsert = function(feature) {
            t.fail("OpenLayers.Layer.Vector.addFeatures calls preFeatureInsert while it must not");
        };
        layer.onFeatureInsert = function(feature) {
            t.fail("OpenLayers.Layer.Vector.addFeatures calls onFeatureInsert while it must not");
        };
        layer.events.register('beforefeatureadded', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.addFeatures triggers beforefeatureadded while it must not");
        });
        layer.events.register('featureadded', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.addFeatures triggers featureadded while it must not");
        });
        layer.events.register('featuresadded', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.addFeatures triggers featuresadded while it must not");
        });

        layer.addFeatures([pointFeature], {silent: true});

        var extent = layer.getDataExtent();
        t.eq(extent.toBBOX(), "-111.04,45.68,-111.04,45.68", "extent from getDataExtent is correct");
    }

    function test_Layer_Vector_getDataExtent(t) {
        t.plan(1);
        var layer = new OpenLayers.Layer.Vector(name);

        var point = new OpenLayers.Geometry.Point(-111.04, 45.68);
        var pointFeature = new OpenLayers.Feature.Vector(point);
        layer.addFeatures([pointFeature]);
        var point = new OpenLayers.Geometry.Point(-111.04, 5.68);
        var pointFeature = new OpenLayers.Feature.Vector(point);
        layer.addFeatures([pointFeature]);
        var extent = layer.getDataExtent();
        t.ok(extent.toBBOX() != layer.features[0].geometry.getBounds().toBBOX(), "extent from getDataExtent doesn't clobber first feature");
    }

    function test_Layer_Vector_removeFeatures(t) {
        t.plan(17);

        var layer = new OpenLayers.Layer.Vector(name);
        var features, log;

        var point1 = new OpenLayers.Geometry.Point(-111.04, 45.68);
        var pointFeature1 = new OpenLayers.Feature.Vector(point1);
        var point2 = new OpenLayers.Geometry.Point(-111.14, 45.78);
        var pointFeature2 = new OpenLayers.Feature.Vector(point2);

        // 1 test
        layer.addFeatures([pointFeature1, pointFeature2]);
        features = layer.removeFeatures([pointFeature1]);
        t.ok(layer.features.length == 1, "OpenLayers.Layer.Vector.removeFeatures removes a feature from the features array");

        // 1 test
        layer.addFeatures([pointFeature1.clone(), pointFeature2.clone()]);
        layer.selectedFeatures.push(layer.features[0]);
        layer.removeFeatures(layer.features[0]);
        t.eq(layer.selectedFeatures, [], "Remove features removes selected features");

        // 1 test
        features = layer.removeFeatures(layer.features);
        t.ok(layer.features.length == 0,
             "OpenLayers.Layer.Vector.removeFeatures(layer.features) removes all feature from the features array");

        // 4 tests
        log = [];
        layer.addFeatures([pointFeature1, pointFeature2]);
        layer.events.register("featuresremoved", null, function(obj) {
            log.push(obj);
        });
        layer.removeFeatures(layer.features);
        t.eq(log.length, 1,
             "\"featuresremoved\" triggered once [0]");
        t.eq(log[0].features.length, 2,
             "\"featuresremoved\" listener is passed two features [0]");
        t.ok(log[0].features[0] == pointFeature1,
             "\"featuresremoved\" listener is passed the correct feature at index 0 [0]");
        t.ok(log[0].features[1] == pointFeature2,
             "\"featuresremoved\" listener is passed the correct feature at index 1 [0]");
        layer.events.remove("featuresremoved");

        // 4 tests
        log = [];
        layer.addFeatures([
            pointFeature1, pointFeature2,
            pointFeature1.clone(), pointFeature2.clone()
        ]);
        layer.selectedFeatures.push(pointFeature1);
        layer.selectedFeatures.push(pointFeature2);
        layer.events.register("featuresremoved", null, function(obj) {
            log.push(obj);
        });
        layer.removeFeatures(layer.selectedFeatures);
        t.eq(log.length, 1,
             "\"featuresremoved\" triggered once [1]");
        t.eq(log[0].features.length, 2,
             "\"featuresremoved\" listener is passed two features [1]");
        t.ok(log[0].features[0] == pointFeature1,
             "\"featuresremoved\" listener is passed the correct feature at index 0 [1]");
        t.ok(log[0].features[1] == pointFeature2,
             "\"featuresremoved\" listener is passed the correct feature at index 1 [1]");
        layer.events.remove("featuresremoved");
        layer.removeFeatures(layer.features);

        // 6 tests
        layer.events.register('beforefeatureremoved', null, function(obj) {
            t.ok(pointFeature1 == obj.feature,
                 "OpenLayers.Layer.Vector.removeFeatures triggers beforefeatureremoved with correct feature passed to callback");
        });
        layer.events.register('featureremoved', null, function(obj) {
            t.ok(pointFeature1 == obj.feature,
                 "OpenLayers.Layer.Vector.removeFeatures triggers featureremoved with correct feature passed to callback");
        });
        layer.events.register('featuresremoved', null, function(obj) {
            t.ok(pointFeature1 == obj.features[0],
                 "OpenLayers.Layer.Vector.removeFeatures triggers featuresremoved with correct features passed to callback");
        });
        layer.addFeatures([pointFeature1]);
        layer.removeFeatures([pointFeature1]);
        layer.addFeatures([pointFeature1]);
        layer.removeFeatures(layer.features);

        // 0 test
        layer.events.register('beforefeatureremoved', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.removeFeatures triggers beforefeatureremoved while it must not");
        });
        layer.events.register('featureremoved', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.removeFeatures triggers featureremoved while it must not");
        });
        layer.events.register('featuresremoved', null, function(obj) {
            t.fail("OpenLayers.Layer.Vector.removeFeatures triggers featuresremoved while it must not");
        });
        layer.addFeatures([pointFeature1]);
        layer.removeFeatures([pointFeature1], {silent: true});
     }

    function test_Layer_Vector_drawFeature(t) {
        t.plan(7);
        var layer = new OpenLayers.Layer.Vector("Test Layer", {isBaseLayer: true});
        var map = new OpenLayers.Map('map', {
            maxExtent: new OpenLayers.Bounds(-100, -100, 100, 100)
        });
        map.addLayer(layer);
        var geometry = new OpenLayers.Geometry.Point(10, 10);
        var feature = new OpenLayers.Feature.Vector(geometry);

        var f, s;

        // Bogus layer renderer needs some methods
        // for functional tests.
        layer.drawn = true;
        layer.renderer = {
            drawFeature: function(feature, style) {
                f = feature;
                s = style;
            },
            root: document.createElement("div"),
            destroy: function() { },
            eraseFeatures: function() {},
            setExtent: function() {}
        };

        layer.drawFeature(feature);
        t.ok(geometry.equals(f.geometry),
             "calls layer.renderer.drawFeature() with feature.geometry");

        feature.style = {foo: "bar"};
        layer.drawFeature(feature);
        t.eq(feature.style, s,
             "calls layer.renderer.drawFeature() with feature.style");

        feature.style = null;
        layer.style = {foo: "bar"};
        layer.drawFeature(feature);
        t.eq(layer.style.foo, s.foo,
             "given null feature style, uses layer style");

        feature.style = {foo1: "bar1"};
        layer.style = {foo2: "bar2"};
        var customStyle = {foo: "bar"};
        layer.drawFeature(feature, customStyle);
        t.eq(customStyle.foo, s.foo,
             "given a custom style, renders with that");

        // the real renderer's drawFeature method is tested in Renderer.html
        layer.renderer.drawFeature = function(feature) {
            return(feature.geometry.getBounds().intersectsBounds(map.getExtent()));
        }
        // reset the drawn to null as if the layer had never been rendered
        layer.drawn = null;

        layer.drawFeature(feature);
        t.ok(true, "Trying to draw a feature on an not drawn layer doesn't throw any error.");

        layer.addFeatures([feature]);

        map.setCenter(new OpenLayers.Bounds(0, 0, 0, 0), 6);
        t.ok(layer.unrenderedFeatures[feature.id], "Did not render feature outside the viewport.");
        map.panTo(new OpenLayers.LonLat(10, 10));
        t.ok(!layer.unrenderedFeatures[feature.id], "Rendered feature inside the viewport.");

        layer.features = [];
    }

    function test_deleted_state(t) {
        t.plan(9);

        var map = new OpenLayers.Map("map");
        var layer = new OpenLayers.Layer.Vector(null, {
            drawn: true
        });
        map.addLayer(layer);
        var feature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(10, 10)
        );
        var log;
        layer.renderer = {
            drawFeature: function(f, s) {
                log = {
                    feature: f,
                    style: s
                };
            },
            destroy: function() {}
        };

        // draw feature with no state
        layer.drawFeature(feature);
        t.ok(log.feature === feature, "[no state] drawFeature called with correct feature");
        t.ok(log.style.display !== "none", "[no state] drawFeature called with style display not none");

        // draw feature with delete style
        feature.state = OpenLayers.State.DELETE;
        layer.drawFeature(feature);
        t.ok(log.feature === feature, "[delete] drawFeature called with correct feature");
        t.eq(log.style.display, "none", "[delete] drawFeature called with style display none");

        // undelete the feature and redraw
        delete feature.state;
        delete feature.renderIntent;
        layer.drawFeature(feature);
        t.ok(log.feature === feature, "[undelete] drawFeature called with correct feature");
        t.ok(log.style.display !== "none", "[undelete] drawFeature called with style display not none");

        // change deleted style
        layer.styleMap.styles["delete"] = new OpenLayers.Style({fillOpacity: 0.1});

        // draw feature with delete style
        feature.state = OpenLayers.State.DELETE;
        layer.drawFeature(feature);
        t.ok(log.feature === feature, "[draw deleted] drawFeature called with correct feature");
        t.ok(log.style.display !== "none", "[draw deleted] drawFeature called with style display not none");
        t.eq(log.style.fillOpacity, 0.1,"[draw deleted] drawFeature called with correct fill opacity");

    }

    function test_Layer_Vector_eraseFeatures(t) {
        t.plan(2);
        var layer = new OpenLayers.Layer.Vector("Test Layer");
        var map = new OpenLayers.Map('map');
        map.addLayer(layer);
        var geometry = new OpenLayers.Geometry.Point(10, 10);
        var feature = new OpenLayers.Feature.Vector(geometry);

        var f;
        layer.renderer = {
            eraseFeatures: function(features) {
                f = features[0];
            },
            destroy: function() { }
        };

        layer.eraseFeatures([feature]);
        t.ok(f, "calls layer.renderer.eraseFeatures");
        t.ok(geometry.equals(f.geometry),
             "calls layer.renderer.eraseFeatures() given an array of features");
    }

    function test_Layer_Vector_destroyFeatures (t) {
        t.plan(5);
        layer = new OpenLayers.Layer.Vector(name);
        var map = new OpenLayers.Map('map');
        map.addLayer(layer);
        var features = [];
        for (var i = 0; i < 5; i++) {
            features.push(new OpenLayers.Feature.Vector(
                          new OpenLayers.Geometry.Point(0,0)));
        }
        layer.addFeatures(features);
        t.eq(layer.features.length, 5, "addFeatures adds 5 features");
        layer.selectedFeatures.push(features[0]);
        layer.destroyFeatures();
        t.eq(layer.features.length, 0, "destroyFeatures triggers removal");
        t.eq(layer.selectedFeatures, [], "Destroy features removes selected features");
        features = [];
        for (var i = 0; i < 5; i++) {
            features.push(new OpenLayers.Feature.Vector(
                          new OpenLayers.Geometry.Point(0,0)));
        }
        layer.addFeatures(features);
        layer.selectedFeatures.push(features[0]);
        layer.selectedFeatures.push(features[1]);
        layer.destroyFeatures([features[0], features[1]]);
        t.eq(layer.features.length, 3, "destroyFeatures removes appropriate features");
        t.eq(layer.selectedFeatures, [], "destroyFeatures removes appropriate selected features");
    }

    function test_Layer_Vector_destroy (t) {
        t.plan(5);

        var options = {protocol: new OpenLayers.Protocol(),
                       strategies: [new OpenLayers.Strategy(), new OpenLayers.Strategy()]}
        var layer = new OpenLayers.Layer.Vector(name, options);
        var map = new OpenLayers.Map('map');
        map.addLayer(layer);
        layer.destroy();
        t.eq(layer.map, null, "layer.map is null after destroy");
        t.eq(layer.getFeatureFromEvent({'target':'map'}), null,
            "getFeatureIdFromEvent doesn't cause an error when called on layer which has been destroyed.");

        t.eq(layer.protocol, null, "layer.protocol is null after destroy");
        t.eq(layer.strategies, null, "layer.strategies is null after destroy");

        // test that we can call layer.destroy a second time without trouble
        try {
            layer.destroy();
            layer.destroy();
            t.ok(true, "layer.destroy called twice without any issues");
        } catch(err) {
            t.fail("calling layer.destroy twice triggers exception: " + err + " in " + err.fileName + " line " + err.lineNumber);
        }

    }

    function test_Layer_Vector_clone(t) {
        t.plan(5);
        var original = new OpenLayers.Layer.Vector(name, {dummyOption: "foo"});
        original.addFeatures([new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1,2), {foo: "bar"})]);
        var clone = original.clone();
        t.ok(clone instanceof OpenLayers.Layer.Vector, "clone is an instance of OpenLayers.Layer.Vector");
        t.ok(clone.name, original.name, "clone has the same name as the original");
        t.ok(clone.features[0] != original.features[0], "clone's feature does not equal the original's feature");
        t.eq(clone.features[0].attributes.foo, original.features[0].attributes.foo, "clone's feature has the same attributes as the original's feature");
        t.eq(clone.dummyOption, original.dummyOption, "clone's dummyOption equals the original's dummy option");
    }

    function test_Layer_Vector_externalGraphic(t) {
        t.plan(11);
        var layer = new OpenLayers.Layer.Vector("Test Layer", {isBaseLayer: true});
        var renderer = layer.renderer;
        var map = new OpenLayers.Map('map');
        map.addLayers([layer]);

        var geometryX = 10;
        var geometryY = 10;
        var geometry = new OpenLayers.Geometry.Point(geometryX, geometryY);
        var feature = new OpenLayers.Feature.Vector(geometry);

        map.zoomToMaxExtent();

        var customStyle1 = new Object({
                externalGraphic: 'test.png',
                pointRadius: 10
        });
        var customStyle2 = new Object({
                externalGraphic: 'test.png',
                graphicWidth: 12
        });
        var customStyle3 = new Object({
                externalGraphic: 'test.png',
                graphicHeight: 14
        });
        var customStyle4 = new Object({
                externalGraphic: 'test.png',
                graphicWidth: 24,
                graphicHeight: 16
        });
        var customStyle5 = new Object({
                externalGraphic: 'test.png',
                graphicWidth: 24,
                graphicOpacity: 1
        });
        var customStyle6 = new Object({
                externalGraphic: 'test.png',
                graphicWidth: 24,
                graphicHeight: 16,
                graphicXOffset: -24,
                graphicYOffset: -16
        });

        var root = renderer.vectorRoot;
        if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.SVG') {
                feature.style = customStyle1;
                layer.drawFeature(feature);
                t.eq(root.firstChild.getAttributeNS(null, 'width'),
                             (2*customStyle1.pointRadius).toString(),
                             "given a pointRadius, width equals 2*pointRadius");
                t.eq(root.firstChild.getAttributeNS(null, 'height'),
                             (2*customStyle1.pointRadius).toString(),
                             "given a pointRadius, height equals 2*pointRadius");
                feature.style = customStyle2;
                layer.drawFeature(feature);
                t.eq(root.firstChild.getAttributeNS(null, 'width'),
                             root.firstChild.getAttributeNS(null, 'height'),
                             "given a graphicWidth, width equals height");
                t.eq(root.firstChild.getAttributeNS(null, 'width'),
                             customStyle2.graphicWidth.toString(),
                             "width is set correctly");
                feature.style = customStyle3;
                layer.drawFeature(feature);
                t.eq(root.firstChild.getAttributeNS(null, 'height'),
                             root.firstChild.getAttributeNS(null, 'width'),
                             "given a graphicHeight, height equals width");
                t.eq(root.firstChild.getAttributeNS(null, 'height'),
                             customStyle3.graphicHeight.toString(),
                             "height is set correctly");
                feature.style = customStyle4;
                layer.drawFeature(feature);
                t.eq(root.firstChild.getAttributeNS(null, 'height'),
                             customStyle4.graphicHeight.toString(),
                             "given graphicHeight and graphicWidth, both are set: height");
                t.eq(root.firstChild.getAttributeNS(null, 'width'),
                             customStyle4.graphicWidth.toString(),
                             "given graphicHeight and graphicWidth, both are set: width");
                feature.style = customStyle5;
                layer.drawFeature(feature);
                t.eq(root.firstChild.getAttributeNS(null, 'style'),
                             'opacity: '+customStyle5.graphicOpacity.toString()+((OpenLayers.Util.getBrowserName() == "opera" || OpenLayers.Util.getBrowserName() == "safari") ? "" : ';'),
                             "graphicOpacity correctly set");
                feature.style = customStyle6;
                layer.drawFeature(feature);
                var x = geometryX / renderer.getResolution() + renderer.left;
                var y = geometryY / renderer.getResolution() - renderer.top;
                // SVG setStyle() gets x and y using getAttributeNS(), which returns
                // a value with only 3 decimal digits. To mimic this we use toFixed(3) here
                x = x.toFixed(3);
                y = y.toFixed(3);
                // toFixed() returns a string
                x = parseFloat(x);
                y = parseFloat(y);
                t.eq(root.firstChild.getAttributeNS(null, 'x'),
                        (x + customStyle6.graphicXOffset).toFixed().toString(),
                        "graphicXOffset correctly set");
                t.eq(root.firstChild.getAttributeNS(null, 'y'),
                        (-y + customStyle6.graphicYOffset).toFixed().toString(),
                        "graphicYOffset correctly set");
        }
        if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.VML') {
                feature.style = customStyle1;
                layer.drawFeature(feature);
                t.eq(root.firstChild.style.width,
                             (2*customStyle1.pointRadius).toString()+'px',
                             "given a pointRadius, width equals 2*pointRadius");
                t.eq(root.firstChild.style.height,
                             (2*customStyle1.pointRadius).toString()+'px',
                             "given a pointRadius, height equals 2*pointRadius");
                feature.style = customStyle2;
                layer.drawFeature(feature);
                t.eq(root.firstChild.style.width,
                             root.firstChild.style.height,
                             "given a graphicWidth, width equals height");
                t.eq(root.firstChild.style.width,
                             customStyle2.graphicWidth.toString()+'px',
                             "width is set correctly");
                feature.style = customStyle3;
                layer.drawFeature(feature);
                t.eq(root.firstChild.style.height,
                             root.firstChild.style.width,
                             "given a graphicHeight, height equals width");
                t.eq(root.firstChild.style.height,
                             customStyle3.graphicHeight.toString()+'px',
                             "height is set correctly");
                feature.style = customStyle4;
                layer.drawFeature(feature);
                var left = parseInt(root.firstChild.style.left);
                var top = parseInt(root.firstChild.style.top);
                t.eq(root.firstChild.style.height,
                             customStyle4.graphicHeight.toString()+'px',
                             "given graphicHeight and graphicWidth, both are set: height");
                t.eq(root.firstChild.style.width,
                             customStyle4.graphicWidth.toString()+'px',
                             "given graphicHeight and graphicWidth, both are set: width");
                feature.style = customStyle5;
                layer.renderer.clear();
                layer.drawFeature(feature);
                var fill = root.firstChild.getElementsByTagName("v:fill")[0];
                var opacity;
                if(fill) {
                    opacity = fill.getAttribute('opacity');
                }
                if(opacity === undefined) {
                    fill = root.firstChild.getElementsByTagName("fill")[0];
                    opacity = fill.getAttribute('opacity');
                }
                t.eq(opacity,
                             customStyle5.graphicOpacity,
                             "graphicOpacity correctly set");
                feature.style = customStyle6;
                layer.drawFeature(feature);
                var offsetLeft = parseInt(root.firstChild.style.left);
                var offsetTop = parseInt(root.firstChild.style.top);
                t.eq((offsetLeft-left)*2, customStyle6.graphicXOffset, "graphicXOffset correctly set");
                t.eq((top-offsetTop)*2, customStyle6.graphicYOffset, "graphicYOffset correctly set");

        }
    }

  </script>
</head>
<body>
<div id="map" style="width:500px;height:550px"></div>
</body>
</html>


text:
Version 1.0 - Barebones

This is an Open Source Project built with React where people can vent their frustrations and later burn their letter. This side project is a work-in-progress and a way for me to sharpen my front-end development skills.

## Roadmap:
- [ ] Random Gifs show up on front page
- [ ] Add SVG/CSS animation to the Burning Letter Page
- [ ] CSS Transitions when switching pages
- [ ] Joke Save Button

Note: This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).

Feedback is appreciated: @saaliklok

redpajama_set_name: RedPajamaGithub


text:
var SpecGrid = Ext.extend(Ext.grid.GridPanel,{
	store:null,
	tariffData:null,//所有产品对应的资费信息
	constructor:function(custIds){
		this.store = new Ext.data.JsonStore({
			baseParams : {
				custId : custIds.join(",")
			},
			url : Constant.ROOT_PATH+"/core/x/Acct!queryBaseProdAcct.action",
			fields:['cust_id','cust_name','acct_id','acctitem_id','user_id','prod_id','tariff_id','tariff_name','tariff_rent','disct_id','disct_name',
					'card_id','prod_sn','acctitem_name','fee','fee_date','invalid_date','real_balance','prod_name',
					'next_tariff_id','next_tariff_name','min_pay','rent','billing_type',
					//缴费金额备份字段
					'feeOther','fee_month']
		});

		this.store.on('load',this.doLoadResult,this);

		var cm = new Ext.ux.grid.LockingColumnModel({
    		defaults : {
				sortable : false
			},
			columns : [
				{header:'客户名',dataIndex:'cust_name',width:105,renderer:App.qtipValue},
				{header:'智能卡号',dataIndex:'card_id',width:150,renderer:App.qtipValue},
				{header:'账目名称',dataIndex:'prod_name',width:85,renderer:App.qtipValue},
				{header:'当前资费',dataIndex:'tariff_name',width:85,renderer:App.qtipValue},
				{header:'未生效资费',dataIndex:'next_tariff_name',width:85,renderer:App.qtipValue},
				{header:'预计到期日',dataIndex:'invalid_date',width:120,renderer:Ext.util.Format.dateFormat},
				{header:'实时余额',dataIndex:'real_balance',width:75,renderer:Ext.util.Format.formatFee}
			]
        });

		SpecGrid.superclass.constructor.call(this,{
			id:'specGridId',
			title:'专用账目缴费',
			store:this.store,
			cm:cm,
			region : 'center',
			border:false,
			view: new Ext.ux.grid.ColumnLockBufferView({}),
			sm:new Ext.grid.RowSelectionModel({})
		});
	},
	initEvents : function(){
		SpecGrid.superclass.initEvents.call(this);
		this.on('afterrender',function(){
			this.swapViews();
		},this,{delay:10})
	},
	swapViews : function(){
		if(this.view.lockedWrap){
			this.view.lockedWrap.dom.style.right = "0px";
		}
        this.view.mainWrap.dom.style.left = "0px";
        if(this.view.updateLockedWidth){
        	this.view.updateLockedWidth = this.view.updateLockedWidth.createSequence(function(){
	            this.view.mainWrap.dom.style.left = "0px";
	        }, this);
        }

	},
	doLoadResult : function(store){
		Ext.getCmp('BusiPanel').hideTip();

		Ext.getCmp('userNum').setValue(store.getCount());
		Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));

		//资费列表 先清空数据
		var tariffNameComp = Ext.getCmp('tariffNameId');
		tariffNameComp.reset();
		var tariffStore = tariffNameComp.getStore();
		tariffStore.removeAll();

		//tariffArr 产品对应的资费信息,tariffIdArr 资费id,用来过滤相同的资费
		var tariffArr = [],tariffIdArr=[];
		Ext.getCmp('specGridId').getStore().each(function(record){
			var data = record.data;
			if(tariffIdArr.indexOf(data['tariff_id']) == -1){//当前资费不存在则存储
				tariffIdArr.push(data['tariff_id']);
				tariffArr.push([data['tariff_id'],data['tariff_name'],data['tariff_rent']]);
			}
		},this);
		tariffStore.loadData(tariffArr);

		//产品对应只有一个资费时,默认选中这个资费,并触发其select事件
		if(tariffStore.getCount()==1){
			tariffNameComp.setValue(tariffStore.getAt(0).get('tariff_id'));
			tariffNameComp.fireEvent('select',tariffNameComp,tariffStore.getAt(0));
		}
	},
	//统一缴费,若选择了折扣,金额必须大于折扣对应的最小数
	//获取编辑表格中缴费列大于零的记录
	getValues:function(){
		var arr = [],data;
		this.store.each(function(record){
			data = record.data;
				var obj = {};
				obj["cust_id"] = data['cust_id'];
				obj["acct_id"] = data['acct_id'];
				obj["acctitem_id"] = data['acctitem_id'];
				obj["user_id"] = data['user_id'];
				obj['card_id'] = data['card_id'];
				obj["prod_sn"] = data['prod_sn'];
				obj["tariff_id"] = data['tariff_id'];
				obj["disct_id"] = data['disct_id'];
				obj["invalid_date"] = data['invalid_date'];
				arr.push(obj);
		},this);
		return arr;
	}
});

SpecForm = Ext.extend(Ext.Panel,{
	rent : null,
	prodStore : null,
	editExpDatePanel : null,
	payPanel : null,
	constructor : function(custIds){
		this.prodStore = new Ext.data.JsonStore({
			autoLoad : true,
			baseParams : {
				custId : custIds.join(",")
			},
			url : Constant.ROOT_PATH+"/core/x/Acct!querySelectableProds.action",
			fields:[{name:'prod_id'},{name:'prod_name'}]
		});

		this.payPanel = {
			layout : 'column',
			anchor : '100%',
			defaults : {
				layout : 'form',
				border :false,
				bodyStyle: "background:#F9F9F9",
				columnWidth : .5
			},
			items : [{
				items : [{
					xtype:'combo',
					id:'oldExpDate',
					fieldLabel : '旧预计到期日',
    				store:new Ext.data.JsonStore({
    					fields:[{name:'exp_date',mapping:0}]
    				}),displayField:'exp_date',valueField:'exp_date',emptyText:'请选择预计到期日',
    				editable:true,forceSelection:true,eableKeyEvents:true
    				,listeners:{
    					scope:this,
    					keyup:this.doKeyupExpDate,
    					select:this.doSelectExpDate
    				}
				},{
					xtype : 'displayfield',
					fieldLabel : '应收金额',
					id : 'shouldPay'
				}]
			},{
				items : [{
					xtype : 'numberfield',
					fieldLabel : '缴费月份数',
					id : 'payMonths',
					value : 1,
					minValue : 1,
					allowBlank : false,
					allowDecimals : false,
					allowNegative:false,
					listeners : {
						scope : this,
						change : this.doMonthChange
					}
				},{
					xtype : 'numberfield',
					fieldLabel : '实收金额',
					id : 'realPay',
					allowBlank : false,
					allowNegative:false
				}]
			}]
		};

		this.editExpDatePanel = {
			xtype : 'panel',
			layout : 'column',
			anchor : '100%',
			defaults : {
				layout : 'form',
				border :false,
				bodyStyle: "background:#F9F9F9",
				columnWidth : .5
			},
			items : [{
				items : [{
					xtype:'combo',
					id:'oldExpDate',
					fieldLabel : '旧预计到期日',
					allowBlank : false,
    				store:new Ext.data.JsonStore({
    					fields:[{name:'exp_date',mapping:0}]
    				}),displayField:'exp_date',valueField:'exp_date',emptyText:'请选择预计到期日',
    				editable:true,forceSelection:true,eableKeyEvents:true
    				,listeners:{
    					scope:this,
    					keyup:this.doKeyupExpDate,
    					select:this.doSelectExpDate
    				}
				}]
			},{
				items : [{
					xtype : 'datefield',
					width : 120,
					allowBlank : false,
					fieldLabel : '预计到期日',
					format : 'Y-m-d',
					id : 'newExpDate'
				}]
			}]
		};

		SpecForm.superclass.constructor.call(this,{
			region : 'south',
			height : 130,
			bodyStyle: Constant.TAB_STYLE,
			layout : 'form',
			defaults : {
				border :false,
				bodyStyle: "background:#F9F9F9"
			},
			items : [{
				id:'tbarCobId',
				xtype:'combo',
				fieldLabel : '账目名称',
    			store: this.prodStore,
    			mode:'local',
    			triggerAction:'all',
    			emptyText:'请选择账目',
    			allowBlank : false,
				displayField:'prod_name',
				valueField:'prod_id',
				editable:true,
				forceSelection:true,
				eableKeyEvents:true,
				selectOnFocus:true
				,listeners:{
					scope:this,
					keyup:this.doKeyupAcctItem,
					select:this.doSelectAcctItem
				}
			},{
				layout : 'column',
				anchor : '100%',
				defaults : {
					layout : 'form',
					border :false,
					bodyStyle: "background:#F9F9F9",
					columnWidth : .5
				},
				items : [{
					items : [{
						xtype:'combo',
						id:'tariffNameId',
						fieldLabel : '资费',
						allowBlank : false,
	    				store:new Ext.data.ArrayStore({
	    					fields:['tariff_id','tariff_name','tariff_rent']
	    				}),displayField:'tariff_name',valueField:'tariff_id',emptyText:'请选择资费',
	    				editable:true,forceSelection:true,eableKeyEvents:true
	    				,listeners:{
	    					scope:this,
	    					keyup:this.doKeyupTariffName,
	    					select:this.doSelectTariffName
	    				}
					}]
				},{
					items : [{
						xtype : 'displayfield',
						id : 'userNum',
						fieldLabel : '户数',
						value : '',
						listeners : {
							scope : this,
							change : this.doUserNumChange
						}
					}]
				}]
				},this.payPanel]
		})

	},
	doKeyupAcctItem:function(combo){
		//清空账目选项时,清空相应资费、折扣列表
		if(Ext.isEmpty(combo.getRawValue())){
			Ext.getCmp('specGridId').getStore().removeAll();//清楚所有过滤器
			var tariffNameComp = Ext.getCmp('tariffNameId');
			tariffNameComp.reset();
			var s = tariffNameComp.getStore();
			s.removeAll();

			Ext.getCmp('userNum').setValue('');
			Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));
		}
	},
	//tbar上账目下拉框select事件
	doSelectAcctItem:function(combo,record){
		var value = combo.getValue();
		var store = Ext.getCmp('specGridId').getStore();
		store.load({
			params : {
				prodId : value
			}
		});
		Ext.getCmp('BusiPanel').showTip();
	},
	//tbar上资费下拉框keyup事件
	doKeyupTariffName:function(combo){
		if(Ext.isEmpty(combo.getRawValue())){
			var store = Ext.getCmp('specGridId').getStore();
			store.clearFilter();

			Ext.getCmp('userNum').setValue(store.getCount());
			Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));
		}
	},
	//tbar上资费下拉框select事件
	doSelectTariffName:function(combo,record){
		var value = combo.getValue();
		if(value){//当选中'资费名称'项时,重新加载grid数据,清空折扣下拉框
			var store = Ext.getCmp('specGridId').getStore();
			store.filter("tariff_id",value);
			Ext.getCmp('userNum').setValue(store.getCount());
			Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));

			var rent = Ext.util.Format.formatFee(record.get('tariff_rent'));
			if(rent == 0){
				this.remove(this.items.itemAt(2));
				this.add(this.editExpDatePanel);
				this.doLayout();

			}else{
				this.remove(this.items.itemAt(2));
				this.add(this.payPanel);

				this.doLayout();

				this.rent = rent;
				var count = parseFloat(Ext.getCmp('userNum').getValue());
				var shouldPay = rent * count;
				if(Ext.getCmp('payMonths').getValue()){
					shouldPay = shouldPay * parseFloat(Ext.getCmp('payMonths').getValue());
				}
				Ext.getCmp('shouldPay').setValue(shouldPay);
				Ext.getCmp("realPay").setValue(shouldPay);
			}

			var arr = Ext.getCmp('specGridId').getStore().collect('invalid_date',false);//所有到期日集合
			var data = [];
			for(var i=0;i<arr.length;i++){
				data.push([arr[i].substring(0,10)]);
			}
			Ext.getCmp('oldExpDate').getStore().loadData(data);

		}
	},
	doKeyupExpDate : function(combo){
		if(Ext.isEmpty(combo.getRawValue())){
			var store = Ext.getCmp('specGridId').getStore();
			store.clearFilter();
			var value = Ext.getCmp('tariffNameId').getValue();
			if(value){
				store.filter("tariff_id",value);
			}

			Ext.getCmp('userNum').setValue(store.getCount());
			Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));
		}
	},
	doSelectExpDate : function(combo){
		if(combo.getValue()){
			var store = Ext.getCmp('specGridId').getStore();
			var value = Ext.getCmp('tariffNameId').getValue();
			store.filterBy(function(record){
	           return record.get('tariff_id') == value && Ext.util.Format.dateFormat(record.get('invalid_date')) == combo.getValue();
	       	});
			Ext.getCmp('userNum').setValue(store.getCount());
			Ext.getCmp('userNum').fireEvent('change',Ext.getCmp('userNum'));

			if(Ext.getCmp('newExpDate')){
				var date = Date.parseDate(combo.getValue(),'Y-m-d');
				Ext.getCmp('newExpDate').setMinValue(date);
			}
		}
	},
	doMonthChange : function(txt){
		if(this.rent && Ext.getCmp('userNum').getValue()){
			var fee = parseFloat(txt.getValue()) * parseFloat(Ext.getCmp('userNum').getValue()) * this.rent;
			Ext.getCmp('shouldPay').setValue(fee);
			Ext.getCmp("realPay").setValue(fee);
		}else{
			Ext.getCmp('shouldPay').setValue("");
			Ext.getCmp("realPay").setValue("");
		}
	},
	doUserNumChange : function(txt){
		if(Ext.getCmp('shouldPay')){
			if(this.rent && txt.getValue()){
				var fee = parseFloat(txt.getValue()) * parseFloat(Ext.getCmp('payMonths').getValue()) * this.rent;
				Ext.getCmp('shouldPay').setValue(fee);
				Ext.getCmp("realPay").setValue(fee);
			}else{
				Ext.getCmp('shouldPay').setValue("");
				Ext.getCmp("realPay").setValue("");
			}
		}
	}
})

SpecPanel = Ext.extend(Ext.Panel,{
	specGrid : null,
	specForm : null,
	constructor : function(){
		var records = App.getApp().main.infoPanel.unitPanel.unitMemberGrid.getSelectionModel().getSelections();
		var custIds = [];
		for(var i=0;i<records.length;i++){
			custIds.push(records[i].get('cust_id'))
		}
		this.specGrid = new SpecGrid(custIds);
		this.specForm = new SpecForm(custIds);
		SpecPanel.superclass.constructor.call(this,{
			layout : 'border',
			border : false,
			items : [this.specGrid,this.specForm]
		})
	},
	getValues : function(){
		return this.specGrid.getValues();
	}
});


text:
<?php

namespace Composer\Command;

use Composer\Factory;
use Composer\IO\IOInterface;
use Composer\Config;
use Composer\Composer;
use Composer\Package\BasePackage;
use Composer\Package\CompletePackageInterface;
use Composer\Repository\CompositeRepository;
use Composer\Repository\RepositoryFactory;
use Composer\Script\ScriptEvents;
use Composer\Plugin\CommandEvent;
use Composer\Plugin\PluginEvents;
use Composer\Util\Filesystem;
use Composer\Util\Loop;
use Composer\Util\ProcessExecutor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Creates an archive of a package for distribution.
 *
 * @author Nils Adermann <naderman@naderman.de>
 */
class ArchiveCommand extends BaseCommand
{
    /**
     * @return void
     */
    protected function configure()
    {
        $this
            ->setName('archive')
            ->setDescription('Creates an archive of this composer package.')
            ->setDefinition(array(
                new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'),
                new InputArgument('version', InputArgument::OPTIONAL, 'A version constraint to find the package to archive'),
                new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip'),
                new InputOption('dir', null, InputOption::VALUE_REQUIRED, 'Write the archive to this directory'),
                new InputOption('file', null, InputOption::VALUE_REQUIRED, 'Write the archive with the given file name.'
                    .' Note that the format will be appended.'),
                new InputOption('ignore-filters', null, InputOption::VALUE_NONE, 'Ignore filters when saving package'),
            ))
            ->setHelp(
                <<<EOT
The <info>archive</info> command creates an archive of the specified format
containing the files and directories of the Composer project or the specified
package in the specified version and writes it to the specified directory.

<info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>

Read more at https://getcomposer.org/doc/03-cli.md#archive
EOT
            )
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $composer = $this->getComposer(false);
        $config = null;

        if ($composer) {
            $config = $composer->getConfig();
            $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'archive', $input, $output);
            $eventDispatcher = $composer->getEventDispatcher();
            $eventDispatcher->dispatch($commandEvent->getName(), $commandEvent);
            $eventDispatcher->dispatchScript(ScriptEvents::PRE_ARCHIVE_CMD);
        }

        if (!$config) {
            $config = Factory::createConfig();
        }

        if (null === $input->getOption('format')) {
            $input->setOption('format', $config->get('archive-format'));
        }
        if (null === $input->getOption('dir')) {
            $input->setOption('dir', $config->get('archive-dir'));
        }

        $returnCode = $this->archive(
            $this->getIO(),
            $config,
            $input->getArgument('package'),
            $input->getArgument('version'),
            $input->getOption('format'),
            $input->getOption('dir'),
            $input->getOption('file'),
            $input->getOption('ignore-filters'),
            $composer
        );

        if (0 === $returnCode && $composer) {
            $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_ARCHIVE_CMD);
        }

        return $returnCode;
    }

    /**
     * @param string|null $packageName
     * @param string|null $version
     * @param string $format
     * @param string $dest
     * @param string|null $fileName
     * @param bool $ignoreFilters
     *
     * @return int
     * @throws \Exception
     */
    protected function archive(IOInterface $io, Config $config, $packageName = null, $version = null, $format = 'tar', $dest = '.', $fileName = null, $ignoreFilters = false, Composer $composer = null)
    {
        if ($composer) {
            $archiveManager = $composer->getArchiveManager();
        } else {
            $factory = new Factory;
            $process = new ProcessExecutor();
            $httpDownloader = Factory::createHttpDownloader($io, $config);
            $downloadManager = $factory->createDownloadManager($io, $config, $httpDownloader, $process);
            $archiveManager = $factory->createArchiveManager($config, $downloadManager, new Loop($httpDownloader, $process));
        }

        if ($packageName) {
            $package = $this->selectPackage($io, $packageName, $version);

            if (!$package) {
                return 1;
            }
        } else {
            $package = $this->getComposer()->getPackage();
        }

        $io->writeError('<info>Creating the archive into "'.$dest.'".</info>');
        $packagePath = $archiveManager->archive($package, $format, $dest, $fileName, $ignoreFilters);
        $fs = new Filesystem;
        $shortPath = $fs->findShortestPath(getcwd(), $packagePath, true);

        $io->writeError('Created: ', false);
        $io->write(strlen($shortPath) < strlen($packagePath) ? $shortPath : $packagePath);

        return 0;
    }

    /**
     * @param string      $packageName
     * @param string|null $version
     *
     * @return (BasePackage&CompletePackageInterface)|false
     */
    protected function selectPackage(IOInterface $io, $packageName, $version = null)
    {
        $io->writeError('<info>Searching for the specified package.</info>');

        if ($composer = $this->getComposer(false)) {
            $localRepo = $composer->getRepositoryManager()->getLocalRepository();
            $repo = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories()));
        } else {
            $defaultRepos = RepositoryFactory::defaultRepos($this->getIO());
            $io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos)));
            $repo = new CompositeRepository($defaultRepos);
        }

        $packages = $repo->findPackages($packageName, $version);

        if (count($packages) > 1) {
            $package = reset($packages);
            $io->writeError('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
            $io->writeError('Alternatives were '.implode(', ', array_map(function ($p) {
                return $p->getPrettyString();
            }, $packages)).'.');
            $io->writeError('<comment>Please use a more specific constraint to pick a different package.</comment>');
        } elseif ($packages) {
            $package = reset($packages);
            $io->writeError('<info>Found an exact match '.$package->getPrettyString().'.</info>');
        } else {
            $io->writeError('<error>Could not find a package matching '.$packageName.'.</error>');

            return false;
        }

        if (!$package instanceof CompletePackageInterface) {
            throw new \LogicException('Expected a CompletePackageInterface instance but found '.get_class($package));
        }

        return $package;
    }
}


text:
/**
 * A tuple of name, email, and date for the author or commit
 * info in a commit.
 */
export class CommitIdentity {
  /**
   * Parses a Git ident string (GIT_AUTHOR_IDENT or GIT_COMMITTER_IDENT)
   * into a commit identity. Throws an error if identify string is invalid.
   */
  public static parseIdentity(identity: string): CommitIdentity {
    // See fmt_ident in ident.c:
    //  https://github.com/git/git/blob/3ef7618e6/ident.c#L346
    //
    // Format is "NAME <EMAIL> DATE"
    //  Markus Olsson <j.markus.olsson@gmail.com> 1475670580 +0200
    //
    // Note that `git var` will strip any < and > from the name and email, see:
    //  https://github.com/git/git/blob/3ef7618e6/ident.c#L396
    //
    // Note also that this expects a date formatted with the RAW option in git see:
    //  https://github.com/git/git/blob/35f6318d4/date.c#L191
    //
    const m = identity.match(/^(.*?) <(.*?)> (\d+) (\+|-)?(\d{2})(\d{2})/)
    if (!m) {
      throw new Error(`Couldn't parse identity ${identity}`)
    }

    const name = m[1]
    const email = m[2]
    // The date is specified as seconds from the epoch,
    // Date() expects milliseconds since the epoch.
    const date = new Date(parseInt(m[3], 10) * 1000)

    if (isNaN(date.valueOf())) {
      throw new Error(`Couldn't parse identity ${identity}, invalid date`)
    }

    // The RAW option never uses alphanumeric timezone identifiers and in my
    // testing I've never found it to omit the leading + for a positive offset
    // but the docs for strprintf seems to suggest it might on some systems so
    // we're playing it safe.
    const tzSign = m[4] === '-' ? '-' : '+'
    const tzHH = m[5]
    const tzmm = m[6]

    const tzMinutes = parseInt(tzHH, 10) * 60 + parseInt(tzmm, 10)
    const tzOffset = tzMinutes * (tzSign === '-' ? -1 : 1)

    return new CommitIdentity(name, email, date, tzOffset)
  }

  public constructor(
    public readonly name: string,
    public readonly email: string,
    public readonly date: Date,
    public readonly tzOffset: number = new Date().getTimezoneOffset()
  ) {}

  public toString() {
    return `${this.name} <${this.email}>`
  }
}


text:
package example.springdata.jpa.customall;

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

/**
 * Integration test showing the usage of a custom method implemented for all repositories
 *
 * @author Oliver Gierke
 * @author Divya Srivastava
 * @soundtrack Elen - It's you (Elen)
 */

@Transactional
@SpringBootTest
class UserRepositoryCustomizationTests {

	@Autowired UserRepository repository;

	@Test
	void invokesCustomMethod() {
		assertThat(repository.customMethod()).isEqualTo(0L);
	}
}


text:
module Language.Armenian(
    armenianLanguage
  , ArmenianToken(..)
  ) where

import Arhelk.Lexer
import Text.Parsec
import TextShow

-- | Extenstion to Token AST
data ArmenianToken = MotiveMark | DependentMark
  deriving (Eq, Show)

instance TextShow ArmenianToken where
  showb t = case t of
    MotiveMark -> "M"
    DependentMark -> "DE"

-- | Lexer specification for Armenian language
armenianLanguage :: LexerLanguage ArmenianToken
armenianLanguage = defaultLexer {
    lexerPunctuation = [
        char '։' >> return EndSentence
      , char ',' >> return Comma
      , (char '․' <|> char '.') >> return Citation
      , char '՝' >> return (ExtToken DependentMark)
      ]
  , lexerInwordMarks = [
        char '՞' >> return QuestionMark
      , char '՜' >> return ExclamationMark
      , char '՛' >> return (ExtToken MotiveMark)
      ]
  }


text:
package DataStructure;

import java.util.Iterator;
/** Iterator used by the LinkedList class. */
public class ListIterator<E> implements Iterator<E> {

	/** The Predecessor of node. */
	private Predecessor<E> prev;

	/**
	 *  The ListNode containing the last element returned, or the
	 *  LinkedList itself if no elements have yet been returned
	 */
	private Predecessor<E> node;

	/** The ListIterator starts at the beginning of the List. */
	public ListIterator(LinkedList<E> list){
		prev = null;
		node = list;
	}

	public boolean hasNext(){
		return node.getNext() != null;
	}

	public E next(){
		prev = node;
		node = node.getNext();
		return ((ListNode<E>) node).getItem();
	}

	public void remove(){
		prev.setNext(node.getNext());
		node = prev;
	}

}