shopr

Check-in [08777612d3]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add items. Start of item menu.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:08777612d33ffb2b2f942e187817ebbdf6da6a87e2c0eb60e1a1bdafe68a2693
User & Date: vandys 2019-06-22 22:41:01
Context
2019-06-23
15:44
Start laying out more menu stuff check-in: 0524a8bdc1 user: vandys tags: trunk
2019-06-22
22:41
Add items. Start of item menu. check-in: 08777612d3 user: vandys tags: trunk
18:23
Filtering bringup check-in: 3848b64d45 user: vandys tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to css/shopr.css.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
48
49
50
51
52
53
54











span {
 font-size: large;
 padding: 4px;
}
#scrlist2 {
 margin-bottom: 15px;
}
#scrlist2 > span, #scrlist2 > input {
 border: 3px solid black;
 border-radius: 6px;
}
#spanR {
 background: pink;
}
#spanG {
................................................................................
 background: yellow;
}
#scrlists2 > span {
 background: pink;
 border: 3px solid black;
 border-radius: 6px;
}


















|







 







>
>
>
>
>
>
>
>
>
>
>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
span {
 font-size: large;
 padding: 4px;
}
#scrlist2 {
 margin-bottom: 15px;
}
#scrlist2 > span, #scrlist2 > input, #scrlist2 > button {
 border: 3px solid black;
 border-radius: 6px;
}
#spanR {
 background: pink;
}
#spanG {
................................................................................
 background: yellow;
}
#scrlists2 > span {
 background: pink;
 border: 3px solid black;
 border-radius: 6px;
}

#scritem {
 background: khaki;
 position: fixed;
 top: 10vh;
 left: 10vw;
 width: 80vw;
 height: 80vh;
 border: 1px solid black;
 z-index: 999;
}

Changes to html/shopr.html.

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46





47
48
49
50
51
52
53
54
 <div id="scrlist2">
  <span style="background: white;">+RGY</span>
  <span id="spanR" onclick="filtR();">R</span>
  <span id="spanG" onclick="filtG();">G</span>
  <span id="spanY" onclick="filtY();">Y</span>
  <input type="text" id="ifilter"
   onkeyup="update_filter();" placeholder="Choose or Add">
  <span>+</span>
  <img src="/imgs/menu.svg" style="float: right;" />
 </div>
 <div id="items">
 </div>
</div>

<div id="scrmenu" style="display: none;">
</div>

<div id="scritem" style="display: none;">





</div>

<script>
init_page();
</script>

</body>
</html>







|










>
>
>
>
>








29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 <div id="scrlist2">
  <span style="background: white;">+RGY</span>
  <span id="spanR" onclick="filtR();">R</span>
  <span id="spanG" onclick="filtG();">G</span>
  <span id="spanY" onclick="filtY();">Y</span>
  <input type="text" id="ifilter"
   onkeyup="update_filter();" placeholder="Choose or Add">
  <button onclick="addit();">+</button>
  <img src="/imgs/menu.svg" style="float: right;" />
 </div>
 <div id="items">
 </div>
</div>

<div id="scrmenu" style="display: none;">
</div>

<div id="scritem" style="display: none;">
<input type="text" placeholder="Item Name" id="itname">
<button onclick="new_iname();">Update Name</button>
<hr>
<button onclick="del_iname();">Delete Item</button>
<button onclick="cancel_scritem();">Cancel</button>
</div>

<script>
init_page();
</script>

</body>
</html>

Changes to items.py.

77
78
79
80
81
82
83



























84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
	    return False
	l = self.items[row]

	# Update item, which flags change to list
	l.assign(col)

	return True




























class Item(object):
    def __init__(self, l, idx, name):

	# Our parent list
	self.parent = l

	# Always start at first index type (usually red)
	# When -1, this is a deleted item
	self.idx = 0

	# Item's string name
	self.name = name

    # JSON friendly rep of our state
    def for_json(self):
	assert self.idx != -1







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|
<
|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
	    return False
	l = self.items[row]

	# Update item, which flags change to list
	l.assign(col)

	return True

    # Add an item to this list
    #
    # Return None on failure, else new Item
    def new_item(self, name):
	# No dups, please
	ln = name.lower()
	for i in self.items:
	    if i.name.lower() == ln:
		return None

	# Create data locally
	i = Item(self, 0, name)

	# Put in DB
	db = self.get_db()
	c = db.cursor()
	c.execute("insert or replace into items values(?,?,?)",
	    (self.lid, name, 0));
	db.commit()
	c.close()
	db.close()

	# Ok
	self.items.append(i)
	self.next_gen()
	return i

class Item(object):
    def __init__(self, l, idx, name):

	# Our parent list
	self.parent = l

	# Current indexed state

	self.idx = idx

	# Item's string name
	self.name = name

    # JSON friendly rep of our state
    def for_json(self):
	assert self.idx != -1

Changes to js/shopr.js.

97
98
99
100
101
102
103





104
105
106





107
108
109
110
111
112
113
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348





















































349
350
351
352
353
354
355
//  the server apparently never heard about the change.
function set_bright(e) {
    e.style.border = "6px solid white";
    cur_bright = e;
    tmo_bright = setTimeout(cancel_bright, 3000);
}






// Long hold; mostly no-op, menu on current element
function hold_item(e) {
    console.log("TBD: hold_item");





}

// Click an item, probably switch the index of the item
function click_item(e) {
    // Tap on already selected item index?
    if (e.textContent) {
	return false;
................................................................................
    // Adjust what's hidden based on latest input
    let ifv = null;
    if (ifilter.value) {
	ifv = ifilter.value.toLowerCase();
    }
    let showing = null;
    for (let i of cur_names.items) {
	if ((ifv == null) || i.name.toLowerCase().contains(ifv)) {
	    showing = "inline";
	} else {
	    showing = "none";
	}

	// Update display attribute
	for (let e of i.elems) {
	    e.style.display = showing;
	}
    }
}






















































// Choose a list
function sel_list(e) {
    cur_list = e.currentTarget.textContent;
    cur_names = null;
    paint_list();
    return false;







>
>
>
>
>
|

<
>
>
>
>
>







 







|











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
118
119
120
121
122
...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
//  the server apparently never heard about the change.
function set_bright(e) {
    e.style.border = "6px solid white";
    cur_bright = e;
    tmo_bright = setTimeout(cancel_bright, 3000);
}

// Back to item list
function cancel_scritem() {
    setcur(scrlist);
}

// Long hold; menu on current element
function hold_item(e) {

    if (!e.id.startsWith("item")) {
	return false;
    }
    itname.value = e.innerText;
    setcur(scritem);
}

// Click an item, probably switch the index of the item
function click_item(e) {
    // Tap on already selected item index?
    if (e.textContent) {
	return false;
................................................................................
    // Adjust what's hidden based on latest input
    let ifv = null;
    if (ifilter.value) {
	ifv = ifilter.value.toLowerCase();
    }
    let showing = null;
    for (let i of cur_names.items) {
	if ((ifv == null) || i.name.toLowerCase().includes(ifv)) {
	    showing = "inline";
	} else {
	    showing = "none";
	}

	// Update display attribute
	for (let e of i.elems) {
	    e.style.display = showing;
	}
    }
}

// Text input, introduce item with this name
function addit() {
    // No text, or only whitespace
    if (!ifilter.value) {
	return false;
    }
    const ifv = ifilter.value.trim();
    if (!ifv) {
	return false;
    }

    // Dup?
    const ifs = ifv.toLowerCase();
    for (let i of cur_names.items) {
	if (i.name.toLowerCase() == ifs) {
	    return false;
	}
    }

    // Ok, new item
    const req = new XMLHttpRequest();
    req.onreadystatechange = () => {
	if (req.readyState != 4) {
	    return;
	}

	if (req.status != 200) {
	    if (req.status == 403) {
		// Changed, password, deleted account, ???
		setcur(scrlogin);
		alert("Login credentials failed, please try again");
	    } else {
		alert("Failed to add item");
	    }
	    return;
	}

	// Server seems happy.  Display update will come via the
	//  usual long polling.
	ifilter.value = "";
	return;
    };

    // Construct URL; our list, authen
    let url = "/l/" + encodeURIComponent(cur_list) +
	".json" + authURL;

    // Requesting URL, including authentication
    req.open("POST", url);
    const op = {"op": "add", "name": ifv};
    req.send(JSON.stringify(op));
}

// Choose a list
function sel_list(e) {
    cur_list = e.currentTarget.textContent;
    cur_names = null;
    paint_list();
    return false;

Changes to main.py.

103
104
105
106
107
108
109


110
111
112
113
114
115
116
	c.execute("select lid,global from lists where owner=? and name=?",
	    (uid, name))
	tup = c.fetchone()
	if tup is not None:
	    lid,isglob = tup
	    res = self.lists[ (uid, name) ] = \
		items.List(self, lid, bool(isglob), name)


	    return res

	# Now global DB's with this name
	c.execute("select lid from lists where name=? and global=1",
	    (name,))
	tup = c.fetchone()
	if tup is None:







>
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
	c.execute("select lid,global from lists where owner=? and name=?",
	    (uid, name))
	tup = c.fetchone()
	if tup is not None:
	    lid,isglob = tup
	    res = self.lists[ (uid, name) ] = \
		items.List(self, lid, bool(isglob), name)
	    if isglob:
		self.lists[name] = res
	    return res

	# Now global DB's with this name
	c.execute("select lid from lists where name=? and global=1",
	    (name,))
	tup = c.fetchone()
	if tup is None:

Changes to post.py.

1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

22


23





































#
# post.py
#	Mixin to implement HTML POST operations
#

import urllib

# The POST part of our handling
class POST_mixin(object):

    # Configure our WPlayer GET treatment
    def __init__(self):

	# POST handlers
	self.dispatchers.append( ("POST", self.post_newlist) )
	self.dispatchers.append( ("POST", self.post_newitem) )

    # A new list requested?
    def post_newlist(self, buf):
	return False,None

    # A new item requested?

    def post_newitem(self, buf):


	return False,None









































>
|









|






>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#
# post.py
#	Mixin to implement HTML POST operations
#
import json, urllib
import pdb

# The POST part of our handling
class POST_mixin(object):

    # Configure our WPlayer GET treatment
    def __init__(self):

	# POST handlers
	self.dispatchers.append( ("POST", self.post_newlist) )
	self.dispatchers.append( ("POST", self.post_op_item) )

    # A new list requested?
    def post_newlist(self, buf):
	return False,None

    # A new item requested?
    # /l/<listname>.json
    def post_op_item(self, buf):
	pp = self.paths
	if (len(pp) != 2) or (pp[0] != "l") or (not pp[1].endswith(".json")):
	    return False,None

	if not self.authenticated():
	    return True,self.send_error(403)

	# List name, without .json
	lname = urllib.unquote_plus(pp[1][:-5]).strip()
	if not lname:
	    return False,None

	# Decode JSON op
	try:
	    req = json.loads(buf)
	    op = str(req["op"])
	    if op == "add":
		name = str(req["name"]).strip()
		if not name:
		    return True,self.send_error(400, "Bad item name")
	    else:
		return True,self.send_error(400, "No such op")
	except:
	    return True,self.send_error(400, "Bad request format")

	# New item
	approot = self.server.approot
	if op == "add":
	    # Under this list
	    l = approot.get_list(self.uid, lname)
	    if l is None:
		return True,self.send_error(404)

	    i = l.new_item(name)
	    if i is None:
		return True,self.send_error(400, "Item add failed")

	    return True,self.send_json({"ok": True})

	return False,None